题目大意:
给定无向图,动态进行删除边,查询两点路径最大边权值。
题解:
1. 首先考虑离线操作倒序进行插入边操作。
2. 之后考虑新加进来的边一定会在原来的最小生成树上产生环,找到环上的最大权删去就可以更新最小生成树。
3. 就相当于加边时判断新加的边两端点在原最小生成树上路径最大值和此边边权关系,新加的小就断开原来的最大边,连接新边。
4. 3的过程可以用LCT维护。
5. LCT只能维护点权,边权需要将边转化成点,把边标号建立成点,连接某两点时直接先连接某一点到边对应的点,再连接边对应点到另一点。
6. 这里有小技巧就是把边对应点和原图上的点标号成一个序列,边对应标号为原边对应标号+n。
7. 不过这里我没用6,我直接进行的标号,这样不是很容易实现,需要多次转化标号。
8. 注意:千万不能把LCT里面写错,否则还得调一天。
9. 这类LCT里面只能对,不能错!!!!
代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
struct node{
int rev,val,nu;//nu:对应原边标号
node *ch[2],*fa,*mx;
int dir(){return this==fa->ch[1];}
void cnct(node *p,int d){ch[d]=p;p->fa=this;}
void down();
void up();
}tnull,*null=&tnull,*tree[1000001],*ed[1000001];
void node::down(){
if(this==null) return;
if(rev){
if(ch[0]!=null) ch[0]->rev^=1;
if(ch[1]!=null) ch[1]->rev^=1;
this->rev=0;
swap(ch[0],ch[1]);
}
}
node *getnode(int k,int l){
node *p=new node;
p->nu=l;
p->rev=0;
p->val=k;
p->mx=p;
p->ch[0]=p->ch[1]=p->fa=null;
return p;
}
void node::up(){
mx=this;
if(ch[0]!=null&&ch[0]->mx->val>mx->val) mx=ch[0]->mx;
if(ch[1]!=null&&ch[1]->mx->val>mx->val) mx=ch[1]->mx;
}
bool isroot(node *p){return p==null||p!=p->fa->ch[0]&&p!=p->fa->ch[1];}
void rotate(node *p){
int d=p->dir();node *x=p->fa;
if(isroot(p)) return;
if(x==x->fa->ch[0])
x->fa->ch[0]=p;
else if(x==x->fa->ch[1])
x->fa->ch[1]=p;
p->fa=x->fa;
x->cnct(p->ch[!d],d);
p->cnct(x,!d);
x->up();
}
void splay(node *p){
static node* sta[1000001];int top=0;
sta[++top]=p;
for(node *k=p;!isroot(k);k=k->fa)
sta[++top]=k->fa;
while(top)
sta[top--]->down();
while(!isroot(p)){
if(isroot(p->fa)) {rotate(p);break;}
else
if(p->dir()==p->fa->dir()) rotate(p->fa),rotate(p);
else rotate(p),rotate(p);
}
p->up();
}
void access(node *p){
node *q=null;
while(p!=null){
splay(p);
p->cnct(q,1);
p->up();
q=p;
p=p->fa;
}
}
void mtr(node *p){
access(p);
splay(p);
p->rev^=1;//!!!!!!!!^=!!!!!!!!
}
void link(node *p,node *q){
mtr(p);
p->fa=q;
}
void cut(node *p,node *q){
mtr(p);
access(q);
splay(q);
q->ch[0]=q->ch[0]->fa=null;
q->up();
}
node* query(node *p,node *q){
mtr(p);
access(q);
splay(q);
return q->mx;
}
node* getroot(node *p){
while(p->fa!=null) p=p->fa;
return p;
}
struct edge{
int from,to,val,d,num;
edge(int a,int b,int c):from(a),to(b),val(c){d=0;}
edge(){d=0;}
bool operator < (edge b) const{//查找插入边在原边标号用的
if(from==b.from)
return to<b.to;
return from<b.from;
}
};
bool cmp1(edge a,edge b){return a.num<b.num;}//按原序排序
bool cmp2(edge a,edge b){return a.val<b.val;}//mst排序
int n,m,Q;
edge e[1000001];
edge pre[1000001];
struct action{
int kd,from,to,ans,num;//num是插入边在原边标号
action(int a,int b,int c):kd(a),from(b),to(c){}
action(){}
};
action q[1000001];
int read(){//这题时间25秒,这个常数可以忽略
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*10+ch-'0';ch=getchar();}
return x*f;
}
int main(){
scanf("%d%d%d",&n,&m,&Q);
for(int i=1;i<=n;i++)
tree[i]=getnode(0,0);
for(int i=1;i<=m;i++){
int a=read(),b=read(),c=read();//scanf("%d%d%d",&a,&b,&c);
ed[i]=getnode(c,i);
if(a>b) swap(a,b);
e[i]=edge(a,b,c);
e[i].num=i;
}
sort(e+1,e+m+1);
for(int i=1;i<=Q;i++){
int a=read(),b=read(),c=read();
// scanf("%d%d%d",&a,&b,&c);
if(b>c) swap(b,c);
q[i]=action(a,b,c);
if(a==2){
int k=lower_bound(e+1,e+m+1,edge(b,c,0))-e;
q[i].num=e[k].num;
e[k].d=1;
}
}
sort(e+1,e+m+1,cmp2);
int cnt=0;
for(int i=1;i<=m;i++)
if(!e[i].d){
edge t=e[i];
if(getroot(tree[t.from])!=getroot(tree[t.to])){
link(tree[t.from],ed[t.num]);
link(tree[t.to],ed[t.num]);
cnt++;
}
if(cnt==n-1) break;
}
sort(e+1,e+m+1,cmp1);
for(int i=Q;i>=1;i--){
if(q[i].kd==1)
q[i].ans=query(tree[q[i].from],tree[q[i].to])->val;
else{
node *p=query(tree[q[i].from],tree[q[i].to]);
if(p->val>e[q[i].num].val){
cut(tree[e[p->nu].from],p);
cut(tree[e[p->nu].to],p);
link(tree[q[i].from],ed[q[i].num]);
link(tree[q[i].to],ed[q[i].num]);
}
}
}
for(int i=1;i<=Q;i++)
if(q[i].kd==1)
printf("%d\n",q[i].ans);
return 0;
}
LCT不能错!!!!不能错!!!!不能错!!!!!