【[WC2006]水管局长】LCT维护最小生成树
*(貌似要省选了?似乎不关我这个蒟蒻的事,还是给OIers们加油吧!)
第一次学到了边转换为点的神奇操作。
luogu传送门:https://www.luogu.org/problemnew/show/P4172
由于要找路径上的最小的最长度(很绕自理解),还是很容易就想到最小生成树的。再由于想到维护最小生成树的时候删边还是很复杂的就想到这道题的删边离线倒着来做,将删边转换成加边来做就吼了。
但是权值全是在边上的,怎么来搞(只会点上的LCT)。。于是翻了题解,发现了神奇的操作---》将边转换成点来操作,边转成的点权值便是边权值,点编号为n+边编号。因此这样就搞定了存储问题。
考虑维护最小生成树,由于是倒着来考虑,因此在结尾是可以用kruskal或者prim来搞出一张最小生成树的图的,这里就直接link一下起点与边点然后link一下边点和终点就可以了。询问时也很简单,setroot(x),access(y),splay(y),return dat[mx[y]]就找到了路径上的最大路。
相对较复杂的是加边操作。不过由于这是一个最小生成树,所以加边(x,y)之后肯定会出现一个环的。而我们先找到这条路径(x,y)上的最大路,判断加的边权是否小于这个最大路,小于就先cut掉最大路,然后link(x,y)进去,否则就不管这个加边,就这样就始终用LCT维护好了一棵最小生成树。
#include<bits/stdc++.h> #define zig(x) zigzag(x,1) #define zag(x) zigzag(x,2) using namespace std; int bingchafa[1005]; int gf(int x) { return bingchafa[x]==x?x:bingchafa[x]=gf(bingchafa[x]); } struct edge { int a,b,c; } bian[200005]; int n,m,q; int bianid[1005][1005],bianxx[200005],bianyy[200005]; int bbian[1005][1005]; struct cz { int k,a,b; }caozuo[200005]; bool operator<(const edge &aa,const edge &bb) { return aa.c<bb.c; } int rev[210005],ls[200005],rs[200005],fa[200005],dat[200005],mx[200005]; int ans[200005]; bool isroot(int x) { return rs[fa[x]]!=x&&ls[fa[x]]!=x; } void putdowm(int x) { if(!rev[x]) return ; swap(ls[x],rs[x]); rev[ls[x]]^=1; rev[rs[x]]^=1; rev[x]=0; } void putup(int x) { mx[x]=x; if(dat[mx[ls[x]]]>dat[mx[x]]) mx[x]=mx[ls[x]]; if(dat[mx[rs[x]]]>dat[mx[x]]) mx[x]=mx[rs[x]]; } void zigzag(int x,int knd) { int y=fa[x],z=fa[y]; if(!isroot(y)) { if(ls[z]==y) ls[z]=x; else rs[z]=x; } fa[x]=z; fa[y]=x; if(knd==1) { ls[y]=rs[x]; fa[ls[y]]=y; rs[x]=y; } else { rs[y]=ls[x]; fa[rs[y]]=y; ls[x]=y; } putup(y); putup(x); } void putdowmall(int x) { if(!isroot(x)) putdowmall(fa[x]); putdowm(x); } void splay(int x) { int y,z; putdowmall(x); while(!isroot(x)) { y=fa[x],z=fa[y]; if(isroot(y)) { if(ls[y]==x) zig(x); else zag(x); } else { if(ls[z]==y) { if(ls[y]==x) { zig(y); zig(x); } else { zag(x); zig(x); } } else { if(rs[y]==x) { zag(y); zag(x); } else { zig(x); zag(x); } } } } } void acc(int x) { for(int y=0;x;y=x,x=fa[x]) { splay(x); rs[x]=y; putup(x); } } void setroot(int x) { acc(x); splay(x); rev[x]^=1; } void link(int x,int y) { setroot(x); fa[x]=y; } void cut(int x,int y) { setroot(x); acc(y); splay(y); ls[y]=fa[x]=0; putup(x); putup(y); } int main() { scanf("%d%d%d",&n,&m,&q); for(int i=1;i<=m;i++) { scanf("%d%d%d",&bian[i].a,&bian[i].b,&bian[i].c); dat[i+n]=bian[i].c; bianid[bian[i].a][bian[i].b]=bianid[bian[i].b][bian[i].a]=i+n; dat[i+n]=bian[i].c; bianxx[i+n]=bian[i].a; bianyy[i+n]=bian[i].b; } for(int i=1;i<=q;i++) { scanf("%d%d%d",&caozuo[i].k,&caozuo[i].a,&caozuo[i].b); if(caozuo[i].k==2) { bbian[caozuo[i].a][caozuo[i].b]=bbian[caozuo[i].b][caozuo[i].a]=1; } } sort(bian+1,bian+1+m); int dian=n-1; int x,y; for(int i=1;i<=n;i++) bingchafa[i]=i; for(int i=1;i<=m;i++) { if(bbian[bian[i].a][bian[i].b]) continue; x=gf(bian[i].a); y=gf(bian[i].b); if(x==y) continue; bingchafa[x]=y; link(bian[i].a,bianid[bian[i].a][bian[i].b]); link(bian[i].b,bianid[bian[i].a][bian[i].b]); dian--; if(!dian) break; } for(int i=q;i>=1;i--) { x=caozuo[i].a; y=caozuo[i].b; if(caozuo[i].k==1) { setroot(x); acc(y); splay(y); ans[i]=dat[mx[y]]; } else { setroot(x); acc(y); splay(y); int idd=bianid[x][y]; if(dat[mx[y]]>dat[idd]) { int z=mx[y]; cut(bianyy[z],z); cut(bianxx[z],z); link(idd,x); link(idd,y); } } } for(int i=1;i<=q;i++) { if(caozuo[i].k==1) printf("%d\n",ans[i]); } }