P4172 [WC2006]水管局长
显然 $LCT$ 动态维护最小生成树
询问就是问树上两点的路径中权值最大的边
为了维护边权,我们要把边也看成点,为了方便,边在 $LCT$ 中的编号为 $n+1$ 到 $n+m$
因为正做不好维护删边,所以离线倒过来,变成加边
在反过来做的时候,为了维护最小生成树要知道哪些边被删除了
用 $set$ 或者 $map$ 并不好维护,但是因为点数不大,所以直接暴力开二维数组就好
注意洛谷数据中可能给的边是 $(u,v)$ ,但是删除的边是 $(v,u)$
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=2e5+7; int c[N][2],fa[N],t[N],val[N];//t维护的是最大值的编号,val是本身的值 bool rev[N]; inline void pushup(int x) { t[x]=x; if(val[ t[c[x][0]] ] > val[t[x]]) t[x]=t[c[x][0]]; if(val[ t[c[x][1]] ] > val[t[x]]) t[x]=t[c[x][1]]; } inline void pushdown(int x) { if(!x||!rev[x]) return; int &lc=c[x][0],&rc=c[x][1]; swap(lc,rc); rev[x]=0; if(lc) rev[lc]^=1; if(rc) rev[rc]^=1; } inline void rever(int x) { rev[x]^=1; pushdown(x); } inline bool noroot(int x) { return (c[fa[x]][0]==x)|(c[fa[x]][1]==x); } inline void rotate(int x) { int y=fa[x],z=fa[y],d=(c[y][1]==x); if(noroot(y)) c[z][c[z][1]==y]=x; fa[x]=z; fa[y]=x; fa[c[x][d^1]]=y; c[y][d]=c[x][d^1]; c[x][d^1]=y; pushup(y); pushup(x); } inline void push_tag(int x) { if(noroot(x)) push_tag(fa[x]); else pushdown(x); pushdown(c[x][0]); pushdown(c[x][1]); } inline void splay(int x) { push_tag(x); while(noroot(x)) { int y=fa[x],z=fa[y]; if(noroot(y)) { if(c[z][0]==y ^ c[y][0]==x) rotate(x); else rotate(y); } rotate(x); } } inline void access(int x) { for(int y=0;x;y=x,x=fa[x]) splay(x),c[x][1]=y,pushup(x); } inline void makeroot(int x) { access(x); splay(x); rever(x); } inline int findroot(int x) { access(x); splay(x); pushdown(x); while(c[x][0]) x=c[x][0],pushdown(x); splay(x); return x; } inline int split(int x,int y) { makeroot(x); access(y); splay(y); return t[y]; }//返回路径上最大边的编号 inline void link(int x,int y) { makeroot(x); if(findroot(y)!=x) fa[x]=y; } inline void cut(int x,int y) { split(x,y); c[y][0]=fa[x]=0; pushup(y); } //以上为LCT模板 int n,m,Q,ans[N]; int mp[1007][1007],id[N],opt[N],dx[N],dy[N];//mp[u][v]维护边(u,v)的编号,id[i]维护第i次操作删的边 //opt,dx,dy存读入数据 bool p[N];//判断边是否被删除 struct edge{ int x,y,v; }e[N];//存边 inline void insert(int k)//加入编号为k的边 { int x=e[k].x,y=e[k].y; bool flag=1; if(findroot(x)==findroot(y)) { int w=split(x,y); if(val[w]>e[k].v) cut(w,e[w-n].x),cut(w,e[w-n].y);//如果更大,删边 else flag=0; } if(flag) link(x,n+k),link(y,n+k);//连边 } int main() { int a,b; n=read(),m=read(),Q=read(); for(int i=1;i<=m;i++) { a=e[i].x=read(),b=e[i].y=read(),e[i].v=read(); mp[a][b]=mp[b][a]=i; val[n+i]=e[i].v; } for(int i=1;i<=Q;i++) { opt[i]=read(),dx[i]=a=read(),dy[i]=b=read(); if(opt[i]==2) p[mp[a][b]]=1,id[i]=mp[a][b]; } for(int i=1;i<=m;i++) if(!p[i]) insert(i); for(int i=Q;i>=1;i--) { if(opt[i]==1) { ans[i]=val[ split(dx[i],dy[i]) ]; continue; } insert(id[i]); } for(int i=1;i<=Q;i++) if(opt[i]==1) printf("%d\n",ans[i]); return 0; }