BZOJ4886 [Lydsy2017年5月月赛]叠塔游戏

时间:2021-04-07 19:16:33

可以发现我们不用管什么权值递增,只要保证横着放的长度互不相同就可以

把权值看成点,一个矩形就是在长和宽之间连一条边

那么我们就要给每条边定一个向,保证每个点出度最多为1,一条有向边的权值就等于其指向的点的权值,最大化边权和

因为题目保证了有解,所以每个连通块要么是树,要么是环套树

因为如果一个连通块里有两个环的话那么至少有一个点出度为二

证明就是如果所有点出度都<=1的话那就是环套树了,只有一个环,矛盾

我们把边对答案的贡献转换到点上,对于环套树连通块,每个点的贡献就是(度数-1)乘权值,对于树的连通块,令权值最大的点为根,他还可以额外做点权那么多的贡献

#include<iostream>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<cstdlib>
#include<cstdio>
#include<map>
#include<bitset>
#include<set>
#include<stack>
#include<vector>
#include<queue>
using namespace std;
#define MAXN 250010
#define MAXM 1010
#define ll long long
#define eps 1e-8
#define MOD 1000000007
#define INF 1000000000
int n;
int a[MAXN],b[MAXN];
int tls[MAXN*2],tln,mx;
map<int,int>h;
int g[MAXN*2];
int f[MAXN*2];
bool d[MAXN*2];
int mxv[MAXN*2];
ll ans;
int fa(int x){
return f[x]==x?x:f[x]=fa(f[x]);
}
int main(){
int i;
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d%d",&a[i],&b[i]);
tls[++tln]=a[i];
tls[++tln]=b[i];
}
sort(tls+1,tls+tln+1);
for(i=1;i<=tln;i++){
if(tls[i]!=tls[i-1]){
g[h[tls[i]]=++mx]=tls[i];
}
}
for(i=1;i<=mx;i++){
f[i]=i;
mxv[i]=g[i];
ans-=g[i];
}
for(i=1;i<=n;i++){
ans+=a[i]+b[i];
int ta=h[a[i]],tb=h[b[i]];
if(fa(ta)!=fa(tb)){
d[fa(tb)]|=d[fa(ta)];
mxv[fa(tb)]=max(mxv[fa(tb)],mxv[fa(ta)]);
f[fa(ta)]=fa(tb);
}else{
d[fa(ta)]=1;
}
}
for(i=1;i<=mx;i++){
if(fa(i)==i&&!d[i]){
ans+=mxv[i];
}
}
printf("%lld\n",ans);
return 0;
}

/*

*/