題意: n個矩形,要求使得上面矩形寬度嚴格小於下面矩形,問能夠得到的最大高度。
思路:
很巧妙的思路。
- 首先題目保證一定有解。考慮將每個矩形的邊(u,v)設置雙向邊u->v與v->u,u->v代表選擇u爲寬,v爲高。
- 那麼可以建出一張圖,這個圖可以分出很多連通分量,不同連通分量意味着每個邊都是不同的,那麼可以在兩個連通分量內分別堆成最高的矩形,然後將兩者合併,結果仍然最優。所以對連通分量可以分別考慮
- 假設連通分量內有n個點,m條邊,代表可以堆m個矩形,最多有n個寬度可以用。要滿足題意,必須使得每個點出度不大於1,則有 m=n 或者 m=n-1。
- 這實際意味着這張圖是樹或者基環樹,然後我們要將每個邊定向,選擇相應的寬高。
- 當這張圖爲基環圖時,每個點都要分一個度數作爲出度(當做寬),剩下度數作爲高,所以每個點的貢獻爲 (度數不作爲出度就得作爲入度)。
- 當這張圖爲樹的時候,此時有一個節點的出度爲0,我們將這個出度爲0的點連到任意其他點,就可以獲得這個點的值(此點值作爲高),我們可以貪心的選擇最大的值。
- 當的時候,圖不連通。當時,要堆個矩形卻只有種寬,不合理。本題巧就巧在保證 都能用上。記得寫過堆積木的題(三維堆箱子求高),那個題範圍比較小可以直接DP。本題2e5,同時加上了巧妙的限制,所以可以用這種建圖的方法解決。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 7;
map<int,int>mp;
int cnt,flag,mx;
int vis[maxn],val[maxn];
int head[maxn],nex[maxn],to[maxn],deg[maxn],tot;
ll ans;
void add(int x,int y) {
if(!mp[x]) mp[x] = ++cnt;
if(!mp[y]) mp[y] = ++cnt;
val[mp[x]] = x;val[mp[y]] = y;
x = mp[x];y = mp[y];
deg[x]++;
to[++tot] = y;
nex[tot] = head[x];
head[x] = tot;
}
void dfs(int x) {
if(vis[x]) return;
vis[x] = 1;
mx = max(mx,val[x]);
ans += 1ll * (deg[x] - 1) * val[x];
flag += (deg[x] - 2);
for(int i = head[x];i;i = nex[i]) {
dfs(to[i]);
}
}
int main() {
int n;scanf("%d",&n);
for(int i = 1;i <= n;i++) {
int x,y;scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
for(int i = 1;i <= cnt;i++) {
if(vis[i]) continue;
flag = mx = 0;
dfs(i);
if(flag < 0) ans += mx;
}
printf("%lld\n",ans);
return 0;
}