link cut tree 入门
鉴于最近写bzoj还有51nod都出现写不动的现象,决定学习一波厉害的算法/数据结构。
link cut tree:研究popoqqq那个神ppt。
bzoj1036:维护access操作就可以了。
#include<cstdio> #include<cstring> #include<cctype> #include<algorithm> #include<queue> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define clr(x,c) memset(x,c,sizeof(x)) #define qwq(x) for(edge *o=head[x];o;o=o->next) int read(){ int x=0,f=1;char c=getchar(); while(!isdigit(c)){ if(c=='-') f=-1;c=getchar(); } while(isdigit(c)) x=x*10+c-'0',c=getchar(); return x*f; } const int nmax=3e4+5; const int inf=0x7f7f7f7f; struct edge{ int to;edge *next; }; edge es[nmax<<1],*pt=es,*head[nmax]; void add(int u,int v){ pt->to=v;pt->next=head[u];head[u]=pt++; pt->to=u;pt->next=head[v];head[v]=pt++; } int n,m,fa[nmax],c[nmax][2],sm[nmax],mx[nmax],q[nmax],w[nmax];bool vis[nmax]; void bfs(){ int l=1,r=1,x;q[r]=1;vis[1]=1; while(l<=r){ x=q[l++]; qwq(x) if(!vis[o->to]) fa[o->to]=x,q[++r]=o->to,vis[o->to]=1; } } bool pdrt(int x){ return c[fa[x]][0]!=x&&c[fa[x]][1]!=x; } void update(int x){ int l=c[x][0],r=c[x][1]; sm[x]=sm[l]+sm[r]+w[x];mx[x]=max(w[x],max(mx[l],mx[r])); } void rotate(int x){ int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1; if(!pdrt(y)) if(c[z][0]==y) c[z][0]=x;else c[z][1]=x; fa[x]=z;fa[y]=x;fa[c[x][r]]=y; c[y][l]=c[x][r];c[x][r]=y; update(y);update(x); } void splay(int x){ //把一个点伸展到它所在的重链的根。 int y,z;//fa[x]=y表示以x为根的splay树的父亲是y,但是y的两个儿子(c[y][0]和c[y][1])中没有一个是x while(!pdrt(x)){ y=fa[x];z=fa[y]; if(!pdrt(y)) if(c[y][0]==x^c[z][0]==y) rotate(x);else rotate(y); rotate(x); } } int access(int x){ int t=0; while(x) splay(x),c[x][1]=t,t=x,update(x),x=fa[x]; return t; //考虑点u,v。假设先access(u)了,那么access(lca(u,v))时lca(u,v)是原splay的根节点, //左边的是深度比它小的点,即是lca(u,v)到根的链。 说明access的操作是正确的。 } void qsum(int u,int v){ access(u);int lca=access(v);splay(u); if(lca==u) printf("%d\n",w[u]+sm[c[lca][1]]); else printf("%d\n",w[lca]+sm[c[lca][1]]+sm[u]); } void qmax(int u,int v){ access(u);int lca=access(v);splay(u); if(lca==u) printf("%d\n",max(w[u],mx[c[lca][1]])); else printf("%d\n",max(w[lca],max(mx[c[lca][1]],mx[u]))); } int main(){ n=read();int u,v,d;mx[0]=-inf; rep(i,1,n-1) u=read(),v=read(),add(u,v); rep(i,1,n) sm[i]=mx[i]=w[i]=read();bfs(); m=read();char ch[11]; rep(i,1,m){ scanf("%s",ch);u=read(),v=read(); if(ch[1]=='H') splay(u),w[u]=v,update(u); else if(ch[1]=='S') qsum(u,v); else qmax(u,v); } return 0; }
bzoj2049:link cut tree 模版题。
#include<cstdio> #include<cstring> #include<cctype> #include<algorithm> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define clr(x,c) memset(x,c,sizeof(x)) #define qwq(x) for(edge *o=head[x];o;o=o->next) int read(){ int x=0,f=1;char c=getchar(); while(!isdigit(c)){ if(c=='-') f=-1;c=getchar(); } while(isdigit(c)) x=x*10+c-'0',c=getchar(); return x*f; } const int nmax=1e4+5; int n,m,fa[nmax],c[nmax][2],st[nmax],rev[nmax]; bool pdrt(int x){ return c[fa[x]][0]!=x&&c[fa[x]][1]!=x; } void pushdown(int x){ if(rev[x]){ int l=c[x][0],r=c[x][1]; rev[x]^=1;rev[l]^=1;rev[r]^=1; swap(c[x][0],c[x][1]); } } void rotate(int x){ int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1; if(!pdrt(y)) if(c[z][0]==y) c[z][0]=x;else c[z][1]=x; fa[x]=z;fa[y]=x;fa[c[x][r]]=y; c[y][l]=c[x][r];c[x][r]=y; } void splay(int x){ int top=0,y,z;st[++top]=x; for(int i=x;!pdrt(i);i=fa[i]) st[++top]=fa[i]; dwn(i,top,1) pushdown(st[i]); while(!pdrt(x)){ y=fa[x];z=fa[y]; if(!pdrt(y)) if(c[y][0]==x^c[z][0]==y) rotate(x);else rotate(y); rotate(x); } } void access(int x){ int t=0; while(x) splay(x),c[x][1]=t,t=x,x=fa[x]; } void rever(int x){ access(x);splay(x);rev[x]^=1; } void link(int x,int y){ rever(x);fa[x]=y; } void cut(int x,int y){ rever(x);access(y);splay(y);c[y][0]=fa[x]=0; } int find(int x){ access(x);splay(x); int y=x;while(c[y][0]) y=c[y][0]; return y; } int main(){ n=read(),m=read();int x,y;char ch[11]; rep(i,1,m){ scanf("%s",ch);x=read(),y=read(); if(ch[0]=='C') link(x,y); else if(ch[0]=='D') cut(x,y); else { if(find(x)==find(y)) puts("Yes"); else puts("No"); } } return 0; }
我这二逼智商。。。真的够了。。。。
upd9.27 重新理解了两次link cut tree 然后打起代码来顺多了。。。
bzoj2002:还是基本的操作
#include<cstdio> #include<cstring> #include<cctype> #include<algorithm> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define clr(x,c) memset(x,c,sizeof(x)) int read(){ int x=0;char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) x=x*10+c-'0',c=getchar(); return x; } const int nmax=2e5+5; const int inf=0x7f7f7f7f; int fa[nmax],c[nmax][2],sz[nmax],q[nmax],nxt[nmax]; bool rev[nmax]; bool isroot(int x){ return c[fa[x]][0]!=x&&c[fa[x]][1]!=x; } void pushup(int x){ sz[x]=sz[c[x][0]]+sz[c[x][1]]+1; } void pushdown(int x){ if(rev[x]){ int l=c[x][0],r=c[x][1]; rev[x]=0;rev[l]^=1;rev[r]^=1; swap(c[x][0],c[x][1]); } } void rotate(int x){ int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1; if(!isroot(y)) c[z][c[z][1]==y]=x; fa[x]=z;fa[y]=x;fa[c[x][r]]=y; c[y][l]=c[x][r];c[x][r]=y; pushup(y);pushup(x); } void splay(int x){ int cnt=0,y,z;q[++cnt]=x; for(int i=x;!isroot(i);i=fa[i]) q[++cnt]=fa[i]; dwn(i,cnt,1) pushdown(q[i]); while(!isroot(x)){ y=fa[x],z=fa[y]; if(!isroot(y)){ if(c[y][0]==x^c[z][0]==y) rotate(x); else rotate(y); } rotate(x); } } void access(int x){ int t=0; while(x) splay(x),c[x][1]=t,t=x,x=fa[x]; //这里应该是要pushup(x)的。但是为什么不加也能A啊TAT } void rever(int x){ access(x),splay(x),rev[x]^=1; } void link(int x,int y){ rever(x);fa[x]=y; } void cut(int x,int y){ rever(x);access(y);splay(y);c[y][0]=fa[x]=0; } int main(){ int n=read(),u,v,d; rep(i,1,n){ u=read();fa[i]=min(n+1,i+u); sz[i]=1;nxt[i]=fa[i]; } sz[n+1]=1; int m=read(); while(m--){ u=read(); if(u==1){ rever(n+1);v=read()+1; access(v);splay(v); printf("%d\n",sz[c[v][0]]); }else{ v=read()+1;d=read();u=min(n+1,v+d); cut(v,nxt[v]);link(v,u);nxt[v]=u; } } return 0; } /* 4 1 2 1 1 3 1 1 2 1 1 1 1 */
bzoj3282:还是基本操作
因为pushup我放在了x=fa[x]后面调了一节晚自修。
#include<cstdio> #include<cstring> #include<cctype> #include<algorithm> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define clr(x,c) memset(x,c,sizeof(x)) int read(){ int x=0;char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) x=x*10+c-'0',c=getchar(); return x; } const int nmax=3e5+5; const int inf=0x7f7f7f7f; int fa[nmax],c[nmax][2],sz[nmax],w[nmax],q[nmax]; bool rev[nmax]; void pushup(int x){ sz[x]=w[x]^sz[c[x][0]]^sz[c[x][1]]; } void pushdown(int x){ if(rev[x]){ int l=c[x][0],r=c[x][1]; rev[x]=0;rev[l]^=1;rev[r]^=1; swap(c[x][0],c[x][1]); } } bool isroot(int x){ return c[fa[x]][0]!=x&&c[fa[x]][1]!=x; } void rotate(int x){ int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1; if(!isroot(y)) c[z][c[z][1]==y]=x; fa[x]=z;fa[y]=x;fa[c[x][r]]=y; c[y][l]=c[x][r];c[x][r]=y; pushup(y);pushup(x); } void splay(int x){ int cnt=0,y,z;q[++cnt]=x; for(int i=x;!isroot(i);i=fa[i]) q[++cnt]=fa[i]; dwn(i,cnt,1) pushdown(q[i]); while(!isroot(x)){ y=fa[x];z=fa[y]; if(!isroot(y)){ if(c[y][0]==x^c[z][0]==y) rotate(x); else rotate(y); } rotate(x); } } void access(int x){ int t=0; while(x) splay(x),c[x][1]=t,pushup(x),t=x,x=fa[x]; } //因为pushup我放在了x=fa[x]后面调了一节晚自修。 void rever(int x){ access(x),splay(x),rev[x]^=1; } void link(int x,int y){ rever(x);fa[x]=y; } void cut(int x,int y){ rever(x);access(y);splay(y);c[y][0]=fa[x]=0; } int find(int x){ access(x);splay(x); while(c[x][0]) x=c[x][0]; return x; } int main(){ int n=read(),m=read(),u,x,y; rep(i,1,n) sz[i]=w[i]=read(); while(m--){ u=read(),x=read(),y=read(); if(u==0) { rever(x);access(y),splay(y); printf("%d\n",sz[y]); }else if(u==1) { if(find(x)!=find(y)) link(x,y); }else if(u==2) { if(find(x)==find(y)) cut(x,y); }else { access(x),splay(x),w[x]=y,pushup(x); } } return 0; }
bzoj2843&1180:还是基本操作
#include<cstdio> #include<cstring> #include<cctype> #include<algorithm> using namespace std; #define rep(i,s,t) for(register int i=s;i<=t;i++) #define dwn(i,s,t) for(register int i=s;i>=t;i--) #define clr(x,c) memset(x,c,sizeof(x)) #define lc c[x][0] #define rc c[x][1] int read(){ int x=0;char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) x=x*10+c-'0',c=getchar(); return x; } const int nmax=3e4+5; const int inf=0x7f7f7f7f; int fa[nmax],c[nmax][2],sm[nmax],w[nmax],q[nmax]; bool rev[nmax]; bool isroot(int x){ int t=x;x=fa[x];return lc!=t&&rc!=t; } void pushup(int x){ sm[x]=w[x]+sm[lc]+sm[rc]; } void pushdown(int x){ if(rev[x]){ rev[x]=0;rev[lc]^=1;rev[rc]^=1; swap(lc,rc); } } void rotate(int x){ int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1; if(!isroot(y)) c[z][c[z][1]==y]=x; fa[x]=z;fa[y]=x;fa[c[x][r]]=y; c[y][l]=c[x][r];c[x][r]=y; pushup(y);pushup(x); } void splay(int x){ int cnt=0,y,z;q[++cnt]=x; for(register int i=x;!isroot(i);i=fa[i]) q[++cnt]=fa[i]; dwn(i,cnt,1) pushdown(q[i]); while(!isroot(x)){ y=fa[x];z=fa[y]; if(!isroot(y)) { if(c[y][0]==x^c[z][0]==y) rotate(x); else rotate(y); } rotate(x); } } void access(int x){ int t=0; while(x) splay(x),c[x][1]=t,pushup(x),t=x,x=fa[x]; } void rever(int x){ access(x);splay(x);rev[x]^=1; } void link(int x,int y){ rever(x);fa[x]=y;if(!(x&233)) splay(x);//都不splayx或者都splayx会比较慢。。。 } void cut(int x,int y){ rever(x);access(y);splay(y);c[y][0]=fa[x]=0; } int find(int x){ access(x);splay(x); while(c[x][0]) x=c[x][0]; return x; } int main(){ int n=read(); rep(i,1,n) w[i]=sm[i]=read(); int m=read(),u,v,d;char s[15]; while(m--){ scanf("%s",s);u=read(),v=read(); if(s[0]=='b') { if(find(u)!=find(v)) link(u,v),puts("yes"); else puts("no"); }else if(s[0]=='e'){ if(find(u)!=find(v)) puts("impossible"); else rever(u),access(v),splay(v),printf("%d\n",sm[v]); }else{ splay(u);w[u]=v;pushup(u); } } return 0; }
bzoj3669:
3669: [Noi2014]魔法森林
Time Limit: 30 Sec Memory Limit: 512 MBSubmit: 2156 Solved: 1310
[Submit][Status][Discuss]
Description
为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M。初始时小E同学在号节点1,隐士则住在号节点N。小E需要通过这一片魔法森林,才能够拜访到隐士。
魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击。幸运的是,在号节点住着两种守护精灵:A型守护精灵与B型守护精灵。小E可以借助它们的力量,达到自己的目的。
只要小E带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无向图中的每一条边Ei包含两个权值Ai与Bi。若身上携带的A型守护精灵个数不少于Ai,且B型守护精灵个数不少于Bi,这条边上的妖怪就不会对通过这条边的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向小E发起攻击,他才能成功找到隐士。
由于携带守护精灵是一件非常麻烦的事,小E想要知道,要能够成功拜访到隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为A型守护精灵的个数与B型守护精灵的个数之和。
Input
第1行包含两个整数N,M,表示无向图共有N个节点,M条边。 接下来M行,第行包含4个正整数Xi,Yi,Ai,Bi,描述第i条无向边。其中Xi与Yi为该边两个端点的标号,Ai与Bi的含义如题所述。 注意数据中可能包含重边与自环。
Output
输出一行一个整数:如果小E可以成功拜访到隐士,输出小E最少需要携带的守护精灵的总个数;如果无论如何小E都无法拜访到隐士,输出“-1”(不含引号)。
WerKeyTom_FTD 神犇的题解:
我们尝试枚举路径的最大a值,那么我们只需按照a排序按顺序插入,维护1到n的b最大值即可。
用并查集维护连通性。当加入j到k这条边时如果形成环,则删除环上的最大值。
我们用动态树来进行维护。
为了实现更易,将每条边看做一个点,例如第i条边两个端点是j与k,那么将i+n与j、k相连,权值放在代表边的点上。
然后莫名其妙的空间开小了居然是TLE重新打一遍的过程中突然想到nmax开小了TAT
#include<cstdio> #include<cstring> #include<cctype> #include<algorithm> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define clr(x,c) memset(x,c,sizeof(x)) int read(){ int x=0;char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) x=x*10+c-'0',c=getchar(); return x; } const int nmax=2e5+5; const int maxn=1e5+5; const int inf=0x7f7f7f7f; int fa[nmax],c[nmax][2],mx[nmax],q[nmax],p[nmax],w[nmax]; bool rev[nmax]; struct edge{ int u,v,a,b; bool operator<(const edge &rhs)const{ return a<rhs.a;} }; edge es[maxn]; bool isroot(int x){ return c[fa[x]][0]!=x&&c[fa[x]][1]!=x; } void pushup(int x){ int l=c[x][0],r=c[x][1]; mx[x]=x; if(w[mx[x]]<w[mx[l]]) mx[x]=mx[l]; if(w[mx[x]]<w[mx[r]]) mx[x]=mx[r]; } void pushdown(int x){ if(rev[x]){ rev[x]=0;rev[c[x][0]]^=1;rev[c[x][1]]^=1; swap(c[x][0],c[x][1]); } } void rotate(int x){ int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1; if(!isroot(y)) c[z][c[z][1]==y]=x; fa[x]=z;fa[y]=x;fa[c[x][r]]=y; c[y][l]=c[x][r];c[x][r]=y; pushup(y);pushup(x); } void splay(int x){ int cnt=0,y,z;q[++cnt]=x; for(register int i=x;!isroot(i);i=fa[i]) q[++cnt]=fa[i]; dwn(i,cnt,1) pushdown(q[i]); while(!isroot(x)){ y=fa[x];z=fa[y]; if(!isroot(y)){ if(c[y][0]==x^c[z][0]==y) rotate(x); else rotate(y); } rotate(x); } } void access(int x){ int t=0; while(x) splay(x),c[x][1]=t,pushup(x),t=x,x=fa[x]; } void rever(int x){ access(x);splay(x);rev[x]^=1; } void link(int x,int y){ rever(x);fa[x]=y;if(x&233) splay(x); } void cut(int x,int y){ rever(x);access(y);splay(y);c[y][0]=fa[x]=0;pushup(y); } int find(int x){ return p[x]==x?x:p[x]=find(p[x]); } int query(int x,int y){ rever(x);access(y),splay(y);return mx[y]; } void mins(int &a,int b){ if(a>b) a=b; } int main(){ int n=read(),m=read(),u,v,d,tmp,temp,ta,tb; rep(i,1,m) es[i].u=read(),es[i].v=read(),es[i].a=read(),es[i].b=read(); sort(es+1,es+m+1); int ans=inf;rep(i,1,n) p[i]=i; rep(i,1,m){ u=es[i].u,v=es[i].v;ta=find(u),tb=find(v); if(ta==tb){ d=query(u,v); if(w[d]>es[i].b) cut(d,es[d-n].u),cut(d,es[d-n].v); else { if(find(1)==find(n)) mins(ans,es[i].a+w[query(1,n)]); continue; } }else p[ta]=tb; w[n+i]=es[i].b;mx[n+i]=n+i; link(u,n+i),link(v,n+i); if(find(1)==find(n)) mins(ans,es[i].a+w[query(1,n)]); } printf("%d\n",ans==inf?-1:ans); return 0; } /* 4 5 1 2 19 1 2 3 8 12 2 4 12 15 1 3 17 8 3 4 1 17 */
bzoj2594:
2594: [Wc2006]水管局长数据加强版
Time Limit: 25 Sec Memory Limit: 128 MBSubmit: 2600 Solved: 840
[Submit][Status][Discuss]
Description
Input
Output
题意:给出一个无向图,边有权值。定义一条路径的长度为该路径所有边的最大值。两种操作:(1)询问u到v所有路径的长度的最小值;(2)删掉某条边。
思路:
(1)将每个边也转化成一个点,比如边(1,2),转化成(1,3),(3,2)两个边;那么边权看做3的点权;1和2的点权可以看做0;
(2)反着做。首先将删掉的边都删掉,询问倒着回答,那么就成了加边。
(3)显然,路径长度就是最小生成树的最大值。因此,首先,将没有删掉的边求一次最小生成树,将最小生成树的边建立动态树。
(4)对于查询比较简单直接查就行。
(5)对于添加边(u,v),若u与v之前没有连通,则该边直接加进去就行,其实就是两个子树连在一起;否则,该边加进去之后会出现环,那么需要删掉环上的最大值的边。
然后因为p[x]习惯性的写成了fa[x]然后调了一节晚自修我TAT
#include<cstdio> #include<cstring> #include<cctype> #include<algorithm> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define clr(x,c) memset(x,c,sizeof(x)) int read(){ int x=0;char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) x=x*10+c-'0',c=getchar(); return x; } const int nmax=1500005; const int maxn=1000005; const int inf=0x7f7f7f7f; struct edge{ int u,v,d,id,f; }; edge es[maxn],ns[100005]; bool cmp(edge a,edge b){ return a.d<b.d; } bool comp(edge a,edge b){ return a.id<b.id; } bool cp(edge a,edge b){ return a.u<b.u||a.u==b.u&&a.v<b.v; } int fa[nmax],c[nmax][2],q[nmax],mx[nmax],w[nmax],ans[nmax],p[nmax];bool rev[nmax]; bool isroot(int x){ return c[fa[x]][0]!=x&&c[fa[x]][1]!=x; } void pushup(int x){ int l=c[x][0],r=c[x][1];mx[x]=x; if(w[mx[x]]<w[mx[l]]) mx[x]=mx[l]; if(w[mx[x]]<w[mx[r]]) mx[x]=mx[r]; } void pushdown(int x){ if(rev[x]){ rev[x]=0;rev[c[x][0]]^=1;rev[c[x][1]]^=1; swap(c[x][0],c[x][1]); } } void rotate(int x){ int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1; if(!isroot(y)) c[z][c[z][1]==y]=x; fa[x]=z;fa[y]=x;fa[c[x][r]]=y; c[y][l]=c[x][r];c[x][r]=y; pushup(y);pushup(x); } void splay(int x){ int cnt=0,y,z;q[++cnt]=x; for(int i=x;!isroot(i);i=fa[i]) q[++cnt]=fa[i]; dwn(i,cnt,1) pushdown(q[i]); while(!isroot(x)){ y=fa[x];z=fa[y]; if(!isroot(y)){ if(c[y][0]==x^c[z][0]==y) rotate(x); else rotate(y); } rotate(x); } } void access(int x){ int t=0; while(x) splay(x),c[x][1]=t,pushup(x),t=x,x=fa[x]; } void rever(int x){ access(x);splay(x);rev[x]^=1; } void link(int x,int y){ rever(x);fa[x]=y; } void cut(int x,int y){ rever(x);access(y);splay(y);c[y][0]=fa[x]=0; } int query(int x,int y){ rever(x);access(y);splay(y);return mx[y]; } int find(int x,int y,int m){ int l=1,r=m,mid; while(l<=r){ mid=(l+r)>>1; if(es[mid].u<x||es[mid].u==x&&es[mid].v<y) l=mid+1; else if(es[mid].u==x&&es[mid].v==y) return mid; else r=mid-1; } } int ff(int x){ return p[x]==x?x:p[x]=ff(p[x]); } int main(){ int n=read(),m=read(),q=read(),tmp,temp,u,v,d,ta,tb; rep(i,1,m) { es[i].u=read(),es[i].v=read(),es[i].d=read(); if(es[i].u>es[i].v) swap(es[i].u,es[i].v); } sort(es+1,es+m+1,cmp); rep(i,1,m) es[i].id=i,w[n+i]=es[i].d,mx[n+i]=n+i; sort(es+1,es+m+1,cp); rep(i,1,q) { ns[i].d=read(),ns[i].u=read(),ns[i].v=read(); if(ns[i].u>ns[i].v) swap(ns[i].u,ns[i].v); if(ns[i].d==2) tmp=find(ns[i].u,ns[i].v,m),es[tmp].f=1,ns[i].id=es[tmp].id; } sort(es+1,es+m+1,comp); int cnt=0;rep(i,1,n) p[i]=i; rep(i,1,m) if(!es[i].f){ u=es[i].u;v=es[i].v;ta=ff(u);tb=ff(v); if(ta!=tb){ p[ta]=tb;link(u,i+n);link(v,i+n);//因为这里的p习惯性的打成了fa调了一节晚自修各种中间输出就是在这里WA然后一直检查模版TAT if(++cnt==n-1) break; } } dwn(i,q,1){ if(ns[i].d==1) ans[i]=w[query(ns[i].u,ns[i].v)]; else{ u=ns[i].u,v=ns[i].v,d=ns[i].id;tmp=query(u,v); if(es[d].d<w[tmp]) { cut(tmp,es[tmp-n].u);cut(tmp,es[tmp-n].v); link(u,d+n);link(v,d+n); } } } rep(i,1,q) if(ns[i].d==1) printf("%d\n",ans[i]); return 0; }