题意
给定
N
N
N 个长方形,第
i
i
i 个长方形大小为
W
i
×
H
i
W_i × H_i
Wi×Hi,每一个长方形可以选择横着摆
放或竖着摆放(一边作为宽,另一边作为高),要将这些长方形堆成一座塔,满足下面
的长方形的宽度要严格大于上面的长方形的宽度。求出用所有的长方形堆出的塔的可
行的最大高度,数据保证存在一组合法解。
输入格式
第一行一个整数 N,描述长方形的数量。
第二行到第 N + 1 行,每行 2 个整数 Wi, Hi,描述一个长方形。
输出格式
一行一个整数,表示用所有方块能堆起来的最大高度。
样例输入
3
50000 160000
50000 100000
50000 100000
样例输出
200000
数据范围
对于 100% 的数据, N ≤ 200000 , 1 ≤ W i , H i ≤ 1 0 5 N \leq 200000, 1 \leq W_i, H_i \leq 10^5 N≤200000,1≤Wi,Hi≤105
题解
我们由于每个底的宽度都不一样。
所以我们考虑想到用权值来算:
一个很奇妙的建图:把一个长方形的2个权值连边。
然后对于建好之后的图,它会有若干个连通块,而且每个连通块都是一颗树或者是基环树。然后对于每一个连通块,如果它是一棵树,设它有
n
n
n个点,
n
−
1
n-1
n−1条边,那么我们对于每个边都要选一个点作为它的底,所以我们一共要选
n
−
1
n-1
n−1个不同的点作为底,剩下的点作为高。由于每个点都会被选至少一次,我们把权值最高的底给扣掉,剩下的就是底的权值,接着把总权值减去底的权值就是答案。
对于一颗基环树,它一共要选
n
n
n个不同的点,所以每个点都要被选一次。剩下的点权值和就是答案
代码如下
#include<bits/stdc++.h>
#define ll long long
#define inf -1e18
using namespace std;
const int N=4e5+10;
inline ll read(){
ll k=0,f=1;
char ch;
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')k=k*10+ch-'0',ch=getchar();
return k*f;
}
ll n,f[N],p[N],cnt,flag,S,mx,sum;
int tot=1,ver[N*4],fst[N*2],nxt[N*4];
map<int ,int> q;
bool v[N];
inline void add(int x,int y){ver[++tot]=y;nxt[tot]=fst[x];fst[x]=tot;}
void dfs(int x,int fa){
//cout<<x<<" "<<p[x]<<endl;
v[x]=1;S+=p[x];mx=max(mx,p[x]);
int CK=0;
for(int i=fst[x];i;i=nxt[i]){
int y=ver[i];
if(y==fa&&!CK){CK++;continue;}
if(v[y]){flag=1;return;}
dfs(y,x);
}
}
int main(){
// freopen("rectangle.in","r",stdin);
// freopen("rectangle.out","w",stdout);
n=read();
for(int i=1;i<=n;++i){
int x=read(),y=read();
sum+=x+y;
if(!q[x])q[x]=++cnt,p[cnt]=x;
if(!q[y])q[y]=++cnt,p[cnt]=y;
add(q[x],q[y]);add(q[y],q[x]);
//cout<<q[x]<<" "<<q[y]<<endl;
}
for(int i=1;i<=cnt;++i){
//cout<<i<<" "<<p[i]<<endl;
if(!v[i]){
mx=-1e9;flag=0,dfs(i,0);
if(!flag)S-=mx;
//cout<<"FAQ "<<i<<" "<<flag<<endl;
}
}
printf("%lld\n",sum-S);
return 0;
}