「BZOJ4886-叠塔游戏」题解
[Lydsy2017年5月月赛]叠塔游戏
state
小Q正在玩一个叠塔的游戏,游戏的目标是叠出尽可能高的塔。在游戏中,一共有\(n\)张矩形卡片,其中第\(i\)张卡片的长度为\(a_i\),宽度为\(b_i\)。
小Q需要把所有卡片按一定顺序叠成一座塔,要求对于任意一个矩形,它的长度要严格大于它上边的任意一个矩形的长度。塔的高度为所有矩形的宽度之和。
在游戏中,小Q可以将卡片翻转90度来使用,而且必须用上全部\(n\)张卡片。
请写一个程序,帮助计算小Q能叠出最高的塔的高度。
第一行包含一个正整数\(n(1<=n<=250000)\),即卡片的个数。
接下来\(n\)行,每行两个正整数\(a_i,b_i(1<=a_i,b_i<=10^9)\),分别表示每张卡片的长度和宽度。
输出一行一个整数,即最高的塔的高度,输入数据保证一定存在解。
样例输入
3
5 16
10 5
5 10
样例输出
20
sol
一个比较巧妙但是典(?)的结论。
对于这种两种状态二选一且要求每类状态只出现一次的问题,可以考虑建图后两个状态连边,改变状态等价于翻转边的方向。建双向边后反边的边权设作操作的代价即可,将边视作有向边,指向的节点视作展示出来的状态,则每个节点入度不得超过 \(1\)。
这道题本质上就是要求所有宽互不相同,最大化长度之和,显然可以转化为上述问题。
初始答案记作所有 \(b\) 之和,然后反边边权设作 \(a-b\)。或者将边权分别设作对应的宽度应该也行,那么就是要找出合法情况下最优边权和。
不难发现,由鸽巢原理可得,连通块内边数不得超过点数,那么就是基环树或树。对于树,换根 DP 枚举入度为 \(0\) 点解决即可;对于基环树,环的方向有两种,环外树的方向必然外向,只有两种情况,二选一即可。
基环树的题码量小是少见的,这道题我代码中两个 DFS 用于基环树找环与计算,两个 DFS 用于换根 DP。但其实没有思维难度,代码难度也不大。
code
const int N=5e5+5;
int n,m;
ll a[N],b[N],c[N];
ll x[N];
int ecnt;
int hd[N],ne[N<<1],to[N<<1],kd[N<<1];ll ed[N<<1];
void adde(int a,int b,ll c,int k){
ne[ecnt]=hd[a];
to[ecnt]=b;
ed[ecnt]=c;
kd[ecnt]=k;
hd[a]=ecnt++;
}
bool vis[N];
pii dfs(int now){
vis[now]=1;
int d=1,c=0;
for(int e=hd[now];~e;e=ne[e]){
int nxt=to[e];
if(!vis[nxt]){
auto res=dfs(nxt);
d+=res.fir,c+=res.sec;
}
c+=kd[e];
}
return {d,c};
}
ll ans;
ll aa,bb,lst;
bool huan[N];
vec<int> path;
bool vis2[N];
bool dfs1(int now,int fe){
path.pub(now);vis2[now]=1;
for(int e=hd[now];~e;e=ne[e]){
if(e==(fe^1))continue;
int nxt=to[e];
if(!vis2[nxt]){if(dfs1(nxt,e))return 1;}
else{
while(1){
int bk=path.back();
path.pob();
huan[bk]=1;lst=bk;
if(bk==nxt)break;
}
return 1;
}
}
path.pob();
return 0;
}
bool vis3[N];
void dfs2(int now,int fe){
vis3[now]=1;
for(int e=hd[now];~e;e=ne[e]){
if(e==(fe^1))continue;
int nxt=to[e];
if(huan[now]&&huan[nxt]){
aa+=ed[e];
bb+=ed[e^1];
}else aa+=ed[e],bb+=ed[e];
if(!vis3[nxt])dfs2(nxt,e);
}
}
void soljh(int x){
aa=bb=0;path.clear();
dfs1(x,-1);
dfs2(lst,-1);
ans+=max(aa,bb);
}
ll f[N][2];
void dfs3(int now,int fid){
for(int e=hd[now];~e;e=ne[e]){
int nxt=to[e];
if(nxt==fid)continue;
dfs3(nxt,now);
f[now][0]+=f[nxt][0]+ed[e];
}
}
ll cc;
void dfs4(int now,int fid){
chmax(cc,f[now][0]+f[now][1]);
for(int e=hd[now];~e;e=ne[e]){
int nxt=to[e];
if(nxt==fid)continue;
f[nxt][1]=f[now][1]+f[now][0]-f[nxt][0]-ed[e]+ed[e^1];
dfs4(nxt,now);
}
}
void soltr(int x){
dfs3(x,0);
cc=-INF;
dfs4(x,0);
ans+=cc;
}
inline void Main(){
read(n);
rep(i,1,n)read(a[i],b[i]),x[++m]=a[i],x[++m]=b[i];
sort(x+1,x+1+m),m=unique(x+1,x+1+m)-x-1;
rep(i,1,m)hd[i]=-1;
rep(i,1,n)a[i]=lower_bound(x+1,x+1+m,a[i])-x,b[i]=lower_bound(x+1,x+1+m,b[i])-x,adde(b[i],a[i],0,1),adde(a[i],b[i],x[a[i]]-x[b[i]],0),ans+=x[b[i]];
rep(i,1,m)if(!vis[i]){
auto res=dfs(i);
if(res.fir==res.sec)soljh(i);
else soltr(i);
}
put(ans);
}

浙公网安备 33010602011771号