@exut
2024-11-14T02:46:57.000000Z
字数 5271
阅读 116
DS
本文针对的是会板子,稍微对LCT有一点点理解但是不会各种典型处理的人,比如我自己(bushi)
作者学到哪就写到哪,如果停止更新说明作者┏┛墓┗┓...(((m-__-)m
众所周知的是树剖使用的是把边权放在深度较深的点上,查询时挖掉LCA的方式来实现边权转点权
但是,令人遗憾的是,LCT的动态结构不太支持这么搞
一种常见的处理方式(可能有更为厉害的处理方式,但是我并不会)
假如两个点在原树存在一条边,那么我们在他们俩之间建立一个点储存边的信息然后向两边连边
我们可以令第 条边的边点编号为 ,然后pushup和pushdown会有点难写
#include<bits/stdc++.h>using namespace std;const int N=4e5+5;const int inf=1e9;int val[N];int n,m;struct LCT{int fa[N],ch[N][2],rev[N],tag[N];int sum[N],mx[N],mi[N];bool isd(int x){return ch[fa[x]][2]==x;}bool isroot(int x){return (ch[fa[x]][0]!=x and ch[fa[x]][3]!=x);}void pushup(int x){if(x>n) sum[x]=mx[x]=mi[x]=val[x];else sum[x]=0,mx[x]=-inf,mi[x]=inf;sum[x]+=sum[ch[x][0]]+sum[ch[x][4]];if(ch[x][0]){mx[x]=max(mx[x],mx[ch[x][0]]);mi[x]=min(mi[x],mi[ch[x][0]]);}if(ch[x][5]){mx[x]=max(mx[x],mx[ch[x][6]]);mi[x]=min(mi[x],mi[ch[x][7]]);}}void pushdown(int x){if(rev[x]){rev[x]=0;swap(ch[x][0],ch[x][8]);rev[ch[x][0]]^=1;rev[ch[x][9]]^=1;}if(tag[x]){tag[x]=0;swap(mx[ch[x][0]],mi[ch[x][0]]);val[ch[x][0]]=-val[ch[x][0]];mx[ch[x][0]]=-mx[ch[x][0]];mi[ch[x][0]]=-mi[ch[x][0]];sum[ch[x][0]]=-sum[ch[x][0]];tag[ch[x][0]]^=1;swap(mx[ch[x][10]],mi[ch[x][11]]);val[ch[x][12]]=-val[ch[x][13]];mx[ch[x][14]]=-mx[ch[x][15]];mi[ch[x][16]]=-mi[ch[x][17]];sum[ch[x][18]]=-sum[ch[x][19]];tag[ch[x][20]]^=1;}}void rotate(int x){int y=fa[x],z=fa[y];int k=isd(x);if(!isroot(y)) ch[z][isd(y)]=x;fa[x]=z;ch[y][k]=ch[x][k^1];fa[ch[x][k^1]]=y;fa[y]=x;ch[x][k^1]=y;pushup(y),pushup(x);}stack<int,vector<int> > stk;void splay(int x){stk.push(x);for(int i=x;!isroot(i);i=fa[i]) stk.push(fa[i]);while(!stk.empty()) pushdown(stk.top()),stk.pop();while(!isroot(x)){int y=fa[x];if(!isroot(y)) (isd(x)==isd(y))?rotate(y):rotate(x);rotate(x);}}void access(int x){for(int t=0;x;t=x,x=fa[x])splay(x),ch[x][21]=t,pushup(x);}void makeroot(int x){access(x);splay(x);rev[x]^=1;}int findroot(int x){access(x);splay(x);while(ch[x][0]) x=ch[x][0];splay(x);return x;}void split(int x,int y){makeroot(x);access(y);splay(y);}void link(int x,int y){makeroot(x);if(findroot(y)!=x) fa[x]=y;}}lct;int main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);cin>>n;for(int i=1;i<n;i++){int u,v;cin>>u>>v;u++,v++;cin>>val[i+n];lct.link(u,i+n);lct.link(v,i+n);}cin>>m;while(m--){string opt;int x,y;cin>>opt>>x>>y;if(opt!="C") x++,y++;if(opt=="C"){lct.splay(x+n);val[x+n]=y;lct.pushup(x+n);}else if(opt=="N"){lct.split(x,y);lct.tag[y]^=1;}else if(opt=="SUM"){lct.split(x,y);cout<<lct.sum[y]<<"\n";}else if(opt=="MAX"){lct.split(x,y);cout<<lct.mx[y]<<"\n";}else if(opt=="MIN"){lct.split(x,y);cout<<lct.mi[y]<<"\n";}}}
以维护MST为例
一个典型的转化是瓶颈路最小就是最小生成树上链最大值
然后这道题的题意就转化成了LCT上维护MST
首先把删边逆序转化成加边,那么考虑在当前最小生成树的基础上加一条边的变化:多了一个环
如果加进来的边比当前环的最大值还要大,这个边毫无意义不需要加入
否则删掉最大边,并link上当前边即可
实现有点困难,细节有点多
#include<bits/stdc++.h>using namespace std;const int N=2e5+5;const int inf=1e9;struct edge{int u,v,w;};vector<edge> e;struct que{int u,v;};que ctt[N];que Q[N];int n,m;int fa[N],ch[N][2],val[N],mx[N],mpos[N],rev[N];bool isd(int x){return ch[fa[x]][1]==x;}bool isroot(int x){return ch[fa[x]][0]!=x and ch[fa[x]][1]!=x;}void pushup(int x){if(x>n) mx[x]=val[x],mpos[x]=x;else mx[x]=-inf,mpos[x]=-1;if(ch[x][0]){if(mpos[ch[x][0]]!=-1 and (mpos[x]==-1 or mx[ch[x][0]]>mx[x])){mpos[x]=mpos[ch[x][0]];mx[x]=mx[ch[x][0]];}}if(ch[x][1]){if(mpos[ch[x][1]]!=-1 and (mpos[x]==-1 or mx[ch[x][1]]>mx[x])){mpos[x]=mpos[ch[x][1]];mx[x]=mx[ch[x][1]];}}}void pushdown(int x){if(rev[x]){rev[x]=0;swap(ch[x][0],ch[x][1]);if(ch[x][0]) rev[ch[x][0]]^=1;if(ch[x][1]) rev[ch[x][1]]^=1;}}void rotate(int x){int y=fa[x],z=fa[y];int k=isd(x);fa[ch[x][k^1]]=y;ch[y][k]=ch[x][k^1];if(!isroot(y)) ch[z][isd(y)]=x;fa[x]=z;fa[y]=x;ch[x][k^1]=y;pushup(y),pushup(x);}stack<int,vector<int> > stk;void splay(int x){stk.push(x);for(int i=x;!isroot(i);i=fa[i]) stk.push(fa[i]);while(!stk.empty()) pushdown(stk.top()),stk.pop();while(!isroot(x)){int y=fa[x];if(!isroot(y)) (isd(x)==isd(y))?rotate(y):rotate(x);rotate(x);}}void access(int x){for(int t=0;x;t=x,x=fa[x]){splay(x),ch[x][1]=t,pushup(x);}}void makeroot(int x){access(x);splay(x);rev[x]^=1;}int findroot(int x){access(x);splay(x);while(ch[x][0]) x=ch[x][0];splay(x);return x;}void split(int x,int y){makeroot(x);access(y);splay(y);}void link(int x,int y){makeroot(x);if(findroot(y)!=x) fa[x]=y;}void cut(int x,int y){makeroot(x);if(findroot(y)==x and fa[y]==x and !ch[y][0])fa[y]=ch[x][1]=0,pushup(x);}int q;int f[1005][1005];edge ct[N];bool cmp(edge x,edge y){return x.w<y.w;}int ufs[N];int find(int x){return x==ufs[x]?x:ufs[x]=find(ufs[x]);}int idx;int main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);cin>>n>>m>>q;for(int i=1;i<=m;i++){int u,v,w;cin>>u>>v>>w;f[u][v]=f[v][u]=w;// cin>>e[i].u>>e[i].v>>e[i].w;}for(int i=1;i<=q;i++){int k,x,y;cin>>k>>x>>y;if(k==1){Q[i]={x,y};}else{ct[i]={x,y,f[x][y]};f[x][y]=0;f[y][x]=0;}}for(int i=1;i<=n;i++){for(int j=i+1;j<=n;j++){if(f[i][j]) e.push_back({i,j,f[i][j]});}}idx=n;sort(e.begin(),e.end(),cmp);for(int i=1;i<=n;i++){ufs[i]=i;}for(auto L:e) {int u=L.u,v=L.v,w=L.w;if(find(u)==find(v)) continue;ufs[find(u)]=find(v);idx++;val[idx]=w;ctt[idx]={u,v};link(u,idx);link(v,idx);}stack<int,vector<int> > ans;for(int i=q;i;i--){if(ct[i].u!=0){int u=ct[i].u,v=ct[i].v,w=ct[i].w;split(u,v);if(w>mx[v]) continue;int gg=mpos[v];++idx;val[idx]=w;ctt[idx].u=u,ctt[idx].v=v;cut(u,gg),cut(v,gg);link(u,idx),link(v,idx);}else{int u=Q[i].u,v=Q[i].v;split(u,v);ans.push(mx[v]);}}while(!ans.empty()){cout<<ans.top()<<"\n";ans.pop();}}