【Luogu】P4172水管局长(LCT)
有个结论是x到y的路径上最长边权值等于最小生成树上最长边权值,于是问题转化为最小生成树。
再考虑把问题反过来,删边变成加边。
于是变成动态维护最小生成树,LCT可以做到。
#include<cstdio> #include<cstdlib> #include<algorithm> #include<cctype> #include<cstring> #include<map> #define maxn 200020 using namespace std; inline long long read(){ long long num=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-') f=-1; ch=getchar(); } while(isdigit(ch)){ num=num*10+ch-'0'; ch=getchar(); } return num*f; } struct Edge{ int from,to,val; bool operator <(const Edge &a)const{ return val<a.val; } }edge[maxn]; int stack[maxn],top; struct Splay{ struct Node{ int e[2],fa,mini,tag,val,maxi; }tree[maxn]; inline int iden(int x){ return x==tree[tree[x].fa].e[1]; } inline void connect(int x,int fa,int how){ tree[x].fa=fa; tree[fa].e[how]=x; } inline bool isroot(int x){ return tree[tree[x].fa].e[0]!=x&&tree[tree[x].fa].e[1]!=x; } inline void update(int x){ int le=tree[x].e[0],ri=tree[x].e[1]; tree[x].maxi=tree[x].val; if(edge[tree[le].maxi].val>edge[tree[x].maxi].val) tree[x].maxi=tree[le].maxi; if(edge[tree[ri].maxi].val>edge[tree[x].maxi].val) tree[x].maxi=tree[ri].maxi; } inline void reverse(int x){ swap(tree[x].e[0],tree[x].e[1]); tree[x].tag^=1; } inline void pushdown(int x){ if(tree[x].tag==0) return; if(tree[x].e[0]) reverse(tree[x].e[0]); if(tree[x].e[1]) reverse(tree[x].e[1]); tree[x].tag=0; } void rotate(int x){ int y=tree[x].fa,r=tree[y].fa; int sony=iden(x),sonr=iden(y); tree[x].fa=r; if(!isroot(y)) tree[r].e[sonr]=x; int b=tree[x].e[sony^1]; connect(b,y,sony); connect(y,x,sony^1); update(y); } inline void pushto(int x){ top=0; while(!isroot(x)){ stack[++top]=x; x=tree[x].fa; } pushdown(x); while(top) pushdown(stack[top--]); } void splay(int x){ pushto(x); while(!isroot(x)){ int fa=tree[x].fa; if(!isroot(fa)) if(iden(fa)==iden(x)) rotate(fa); else rotate(x); rotate(x); } update(x); } inline void access(int x){ int last=0; while(x){ splay(x); tree[x].e[1]=last; update(x); last=x; x=tree[x].fa; } } inline void makeroot(int x){ access(x); splay(x); reverse(x); } inline int findroot(int x){ access(x); splay(x); while(tree[x].e[0]) x=tree[x].e[0]; return x; } inline void split(int x,int y){ makeroot(x); access(y); splay(y); } inline void link(int x,int y){ //printf("%d %d\n",x,y); split(x,y); tree[x].fa=y; } inline void cut(int x,int y){ //printf("%d %d\n",x,y); split(x,y); if(tree[y].e[0]!=x||tree[x].e[1]) return; tree[x].fa=tree[y].e[0]=0; } }s; struct Que{ int opt,x,y,id,cnt; }q[maxn]; int cnt; int ans[maxn]; int d[maxn*10]; bool vis[maxn]; int main(){ int n=read(),m=read(),e=read(); for(int i=1;i<=m;++i){ edge[i]=(Edge){read(),read(),read()}; if(edge[i].from>edge[i].to) swap(edge[i].from,edge[i].to); } sort(edge+1,edge+m+1); for(int i=1;i<=m;++i){ //printf("%d %d>><\n",edge[i].from,edge[i].to); s.tree[i+n].mini=s.tree[i+n].val=i; d[edge[i].from*n+edge[i].to]=i; } for(int i=1;i<=e;++i){ q[i]=(Que){read(),read(),read(),0}; if(q[i].x>q[i].y) swap(q[i].x,q[i].y); if(q[i].opt==1) q[i].id=++cnt; else{ int now=d[q[i].x*n+q[i].y]; q[i].cnt=now; //printf("%d\n",now); vis[now]=1; } } int sum=0; for(int i=1;i<=m;++i){ if(sum==n-1) break; if(vis[i]) continue; if(s.findroot(edge[i].from)==s.findroot(edge[i].to)) continue; s.link(edge[i].from,i+n); s.link(edge[i].to,i+n); sum++; } for(int i=e;i;--i){ if(q[i].opt==1){ s.split(q[i].x,q[i].y); ans[q[i].id]=edge[s.tree[q[i].y].maxi].val; } else{ s.split(q[i].x,q[i].y); int now=q[i].cnt; int ret=s.tree[q[i].y].maxi; if(edge[now].val<edge[ret].val){ if(edge[ret].from) s.cut(edge[ret].from,ret+n); if(edge[ret].to) s.cut(edge[ret].to,ret+n); s.link(q[i].x,now+n); s.link(q[i].y,now+n); } } } for(int i=1;i<=cnt;++i) printf("%d\n",ans[i]); return 0; }