————学习LCT的同学建议去研究杨哲大神的《QTREE解法的一些研究》,自觉讲得十分详细。
LCT的核心算法也就是几个过程:access.makeroot,link,cut……
搞出这几个过程LCT也不是啥难写的东西啦。
在最近做了几道入门题,奉上。
spoj qtree3
树上支持修改颜色(黑白),查询路径上第一个黑点编号。
直接LCT搞吧,记录路径上黑点的数量。修改时把x店转到更直接改,查询时在树上走直到第一个黑点即可。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int Maxn=100005; #define isroot(x) ((son[fa[x]][0]!=x) && (son[fa[x]][1]!=x)) #define update(x) sum[x]=sum[son[x][0]]+sum[son[x][1]]+flag[x] int fa[Maxn],son[Maxn][2],rev[Maxn],sum[Maxn],flag[Maxn]; int n,q,i,K,x,y; void push(int x){ swap(son[x][0],son[x][1]); if (son[x][0]>0) rev[son[x][0]]^=1; if (son[x][1]>0) rev[son[x][1]]^=1; rev[x]=0; } void rotate(int x,int f,int K){ if (!isroot(f)){ if (son[fa[f]][0]==f) son[fa[f]][0]=x; else son[fa[f]][1]=x; } fa[x]=fa[f]; if (son[x][K^1]>0) fa[ son[x][K^1] ]=f; son[f][K]=son[x][K^1]; son[x][K^1]=f; fa[f]=x; update(f); } void Splay(int x){ int f, gf; while (!isroot(x)){ f=fa[x]; gf=fa[f]; if (rev[gf]) push(gf); if (rev[f]) push(f); if (rev[x]) push(x); if (isroot(f)){ if (son[f][0]==x) rotate(x,f,0); if (son[f][1]==x) rotate(x,f,1); } else { if (son[f][0]==x && son[gf][0]==f) rotate(f,gf,0), rotate(x,f,0); if (son[f][1]==x && son[gf][1]==f) rotate(f,gf,1), rotate(x,f,1); if (son[f][0]==x && son[gf][1]==f) rotate(x,f,0), rotate(x,gf,1); if (son[f][1]==x && son[gf][0]==f) rotate(x,f,1), rotate(x,gf,0); } } if (rev[x]) push(x); update(x); } void access(int x){ int t=0; while (x){ Splay(x); son[x][1]=t; update(x); t=x; x=fa[x]; } } void makeroot(int x) { access(x); Splay(x); rev[x]^=1; } void Link(int x,int y) { makeroot(x); fa[x]=y; access(x);} void cut(int x,int y){ makeroot(x); access(y); Splay(y); fa[son[y][0]]=0; son[y][0]=0; Splay(x); Splay(y); } int main(){ freopen("qtree3.in","r",stdin); freopen("qtree3.out","w",stdout); scanf("%d%d",&n,&q); for (i=1;i<n;i++){ scanf("%d%d",&x,&y); Link(x,y); } for (i=1;i<=q;i++){ scanf("%d%d",&K,&x); if (K==0){ makeroot(x); flag[x]^=1; update(x); } else { makeroot(1); access(x); Splay(x); if (sum[x]==0) {puts("-1");continue;} while (sum[x]>0){ if (rev[x]) push(x); if (sum[son[x][0]]>0) x=son[x][0]; else if (flag[x]==1) break; else x=son[x][1]; } printf("%d\n",x); } } return 0; }
spoj gss7
树上支持修改整个路径上的值,询问路径上最大子序列和。
LCT+标记大法!像gss1一样维护最大子序列和,把它套到LCT里就好了
//注意标记的传递顺序。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int Maxn=100005; #define isroot(x) ((son[fa[x]][0]!=x)&&(son[fa[x]][1]!=x)) int son[Maxn][2],fa[Maxn],rev[Maxn],size[Maxn],sum[Maxn]; int tip[Maxn],lef[Maxn],rig[Maxn],maxx[Maxn],w[Maxn]; int n,m,i,x,y,z,K; void push1(int x){ swap(son[x][0],son[x][1]); //swap(lef[x],rig[x]); if (son[x][0]>0){ rev[son[x][0]]^=1; swap(lef[son[x][0]],rig[son[x][0]]); } if (son[x][1]>0){ rev[son[x][1]]^=1; swap(lef[son[x][1]],rig[son[x][1]]); } rev[x]=0; } void push2(int x){ int y; if (son[x][0]>0){ y=son[x][0]; w[y]=tip[y]=tip[x]; sum[y]=w[y]*size[y]; if (tip[y]<0) maxx[y]=lef[y]=rig[y]=0; else maxx[y]=lef[y]=rig[y]=tip[y]*size[y]; } if (son[x][1]>0){ y=son[x][1]; w[y]=tip[y]=tip[x]; sum[y]=w[y]*size[y]; if (tip[y]<0) maxx[y]=lef[y]=rig[y]=0; else maxx[y]=lef[y]=rig[y]=tip[y]*size[y]; } tip[x]=-10001; } void update(int x){ size[x]=size[son[x][0]]+size[son[x][1]]+1; sum[x]=sum[son[x][0]]+sum[son[x][1]]+w[x]; lef[x]=max(lef[son[x][0]],sum[son[x][0]]+w[x]+lef[son[x][1]]); rig[x]=max(rig[son[x][1]],sum[son[x][1]]+w[x]+rig[son[x][0]]); maxx[x]=max(maxx[son[x][0]],maxx[son[x][1]]); maxx[x]=max(maxx[x],rig[son[x][0]]+w[x]+lef[son[x][1]]); } void rotate(int x,int f,int K){ if (!isroot(f)){ if (son[fa[f]][0]==f) son[fa[f]][0]=x; else son[fa[f]][1]=x; } fa[x]=fa[f]; if (son[x][K^1]>0) fa[son[x][K^1]]=f; son[f][K]=son[x][K^1]; fa[f]=x; son[x][K^1]=f; update(f); } void Splay(int x){ int f, gf; while (!isroot(x)){ f=fa[x]; gf=fa[f]; if (rev[gf]>0) push1(gf); if (rev[f]>0) push1(f); if (rev[x]>0) push1(x); if (tip[gf]>-10001) push2(gf); if (tip[f]>-10001) push2(f); if (tip[x]>-10001) push2(x); if (isroot(f)){ if (son[f][0]==x) rotate(x,f,0); if (son[f][1]==x) rotate(x,f,1); } else { if (son[f][0]==x && son[gf][0]==f) rotate(f,gf,0), rotate(x,f,0); if (son[f][1]==x && son[gf][1]==f) rotate(f,gf,1), rotate(x,f,1); if (son[f][0]==x && son[gf][1]==f) rotate(x,f,0), rotate(x,gf,1); if (son[f][1]==x && son[gf][0]==f) rotate(x,f,1), rotate(x,gf,0); } } if (rev[x]>0) push1(x); if (tip[x]>-10001) push2(x); update(x); } void access(int x){ int t=0; while (x){ Splay(x); son[x][1]=t; update(x); t=x; x=fa[x]; } } void makeroot(int x) { access(x); Splay(x); rev[x]^=1; swap(lef[x],rig[x]); } void Link(int x,int y) { makeroot(x); fa[x]=y; access(x); } void cut(int x,int y){ makeroot(x); access(y); Splay(y); fa[ son[y][0] ]=0; son[y][0]=0; Splay(x); Splay(y); } int main(){ freopen("gss7.in","r",stdin); freopen("gss7.out","w",stdout); scanf("%d",&n); for (i=1;i<=n;i++){ scanf("%d",&w[i]); size[i]=1; if (w[i]<0) continue; sum[i]=lef[i]=rig[i]=maxx[i]=w[i]; } memset(tip,-10,sizeof(tip)); for (i=1;i<n;i++){ scanf("%d%d",&x,&y); Link(x, y); } scanf("%d",&m); for (i=1;i<=m;i++){ scanf("%d",&K); if (K==1){ scanf("%d%d",&x,&y); makeroot(x); access(y); Splay(y); printf("%d\n",maxx[y]); } else { scanf("%d%d%d",&x,&y,&z); makeroot(x); access(y); Splay(y); w[y]=tip[y]=z; sum[y]=z*size[y]; if (z<0) maxx[y]=lef[y]=rig[y]=0; else maxx[y]=lef[y]=rig[y]=z*size[y]; } } return 0; }
//各种骗分ac的算法就不说了。。。。
这题正解是LCT。
将边按a值从小到大排序,顺序扫描加入,维护MST。
为了方便利用LCT,我们把每条边看作一个虚点,其权值为这条边的b值,他与原图中的x,y相连,原图中的点权值就记为0吧。
每次加入一条边,如果x,y还未联通,直接把他们连起来,否则看x,y这条链上的最大值是否大于这条边的权值,去较小的保留。
每加完一条边求出a+(1~n的最大值),取其中最小值即为答案。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define isroot(x) ((son[fa[x]][0]!=x)&&(son[fa[x]][1]!=x)) #define update(x) maxx[x]=max(w[x], max(maxx[son[x][0]],maxx[son[x][1]]) ) const int Maxn=200005; int w[Maxn],rev[Maxn],son[Maxn][2],fa[Maxn]; int maxx[Maxn],F[Maxn],n,m,ans,i,t,m1,m2; struct EDGE { int x,y,a,b; void read(){ scanf("%d%d%d%d",&x,&y,&a,&b); } bool operator <(const EDGE &r)const { return a<r.a;} }e[Maxn]; void push(int x){ swap(son[x][0],son[x][1]); if (son[x][0]>0) rev[son[x][0]]^=1; if (son[x][1]>0) rev[son[x][1]]^=1; rev[x]=0; } void rotate(int x,int f,int K){ if (!isroot(f)){ if (son[fa[f]][0]==f) son[fa[f]][0]=x; else son[fa[f]][1]=x; } fa[x]=fa[f]; if (son[x][K^1]>0) fa[ son[x][K^1] ]=f; son[f][K]=son[x][K^1]; son[x][K^1]=f; fa[f]=x; update(f); } void Splay(int x){ int f, gf; while (!isroot(x)){ f=fa[x]; gf=fa[f]; if (rev[gf]>0) push(gf); if (rev[f]>0) push(f); if (rev[x]>0) push(x); if (isroot(f)){ if (son[f][0]==x) rotate(x,f,0); if (son[f][1]==x) rotate(x,f,1); } else { if (son[f][0]==x && son[gf][0]==f) rotate(f,gf,0), rotate(x,f,0); if (son[f][1]==x && son[gf][1]==f) rotate(f,gf,1), rotate(x,f,1); if (son[f][0]==x && son[gf][1]==f) rotate(x,f,0), rotate(x,gf,1); if (son[f][1]==x && son[gf][0]==f) rotate(x,f,1), rotate(x,gf,0); } } if (rev[x]>0) push(x); update(x); } void access(int x){ int t=0; while (x){ Splay(x); son[x][1]=t; update(x); t=x; x=fa[x]; } } void makeroot(int x) { access(x); Splay(x); rev[x]^=1; } void Link(int x,int y) { makeroot(x); fa[x]=y; access(x); } void cut(int x,int y){ makeroot(x); access(y); Splay(y); fa[ son[y][0] ]=0; son[y][0]=0; Splay(x); Splay(y); } int getit(int x){ int d=maxx[x]; while (true){ if (rev[x]>0) push(x); if (w[x]==d) break; if (maxx[son[x][0]]==d) x=son[x][0]; else x=son[x][1]; } return x-n; } int findf(int x){ int xx=x, xxx; while (xx!=F[xx]) xx=F[xx]; while (x!=xx) xxx=x, x=F[x], F[xxx]=xx; return xx; } int main(){ //freopen("forest.in","r",stdin); //freopen("forest.out","w",stdout); scanf("%d%d",&n,&m); for (i=1;i<=m;i++) e[i].read(); sort(e+1,e+m+1); for (i=1;i<=m;i++) maxx[i+n]=w[i+n]=e[i].b; ans=(1<<30); for (i=1;i<=n;i++) F[i]=i; for (i=1;i<=m;i++){ m1=findf(e[i].x); m2=findf(e[i].y); if (m1!=m2){ F[m1]=m2; Link(e[i].x,i+n); Link(e[i].y,i+n); } else{ makeroot(e[i].x); access(e[i].y); Splay(e[i].x); if (maxx[e[i].x]>e[i].b){ t=getit(e[i].x); cut(e[t].x,t+n); cut(e[t].y,t+n); Link(e[i].x,i+n); Link(e[i].y,i+n); } } m1=findf(1); m2=findf(n); if (m1==m2){ makeroot(1); access(n); Splay(1); if (ans>e[i].a+maxx[1]) ans=e[i].a+maxx[1]; } } if (ans==(1<<30)) ans=-1; printf("%d\n",ans); return 0; }