LCT总结
学LCT一段时间了我深深的理解其恶心之处 是代码写的让我难受,可能是一天写了3次LCT。
我 真的不想写了 况且期末考崩了,我 估计要GG了。完蛋!
这里先列举几个我经常犯的错误: 1 LCT和并查集混在一起fa数组写成f数组。
2 两点之间连边的时候必要的要去make_root其中一个点再连边不然是不符合LCT的性质的(深度的单调性)。
3 cut的时候注意make_root(x) access(y)了之后注意x不在splay中的根了!
4 注意一些小细节如pushdown和rotate不要写错。
入门题目 好像分块也是可以写的 但是实现起来不如splay方便这里维护各棵树即可。
修改的话直接修改 查询直接询问当前点到根之间的点有多少个很好实现。
//#include<bits/stdc++.h> #include<iostream> #include<iomanip> #include<cstdio> #include<cstring> #include<string> #include<vector> #include<queue> #include<deque> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<cctype> #include<utility> #include<map> #include<set> #include<bitset> #include<cmath> #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define INF 1000000000 #define ll long long #define db double #define RI register int using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } inline void put(int x) { x<0?putchar('-'),x=-x:0; int num=0;char ch[50]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); putchar('\n');return; } //LCT const int MAXN=200010; int n,m; int a[MAXN]; int c[MAXN][2],f[MAXN],b[MAXN],s[MAXN]; inline int get(int x){return c[f[x]][0]==x||c[f[x]][1]==x;} inline void pushup(int x) { s[x]=s[c[x][0]]+s[c[x][1]]+1; } inline void rotate(int x) { int old=f[x],oldf=f[old],k=c[old][1]==x; if(get(old))c[oldf][c[oldf][1]==old]=x; c[old][k]=c[x][k^1];c[x][k^1]=old; if(c[old][k])f[c[old][k]]=old; f[old]=x;f[x]=oldf;pushup(old); } inline void splay(int x) { while(get(x)) { int old=f[x],oldf=f[old]; if(get(old))rotate((c[old][0]==x)^(c[oldf][0]==old)?x:old); rotate(x); } pushup(x); } inline void access(int x) { for(int y=0;x;x=f[y=x]) splay(x),c[x][1]=y,pushup(x); } inline void cut(int x,int y) { access(x);splay(x); c[x][0]=f[c[x][0]]=0;pushup(x); } int main() { //freopen("1.in","r",stdin); //freopen("1.out","w",stdout); n=read(); for(int i=1;i<=n;++i) { s[i]=1;a[i]=read(); if(i+a[i]<=n)f[i]=i+a[i]; } m=read(); for(int i=1;i<=m;++i) { int p,x,y; p=read();x=read()+1; if(p==1) { access(x);splay(x); put(s[x]); } else { y=read(); int last=a[x];a[x]=y; cut(x,x+last>n?0:x+last); if(x+a[x]<=n)f[x]=x+a[x]; } } return 0; }
动态的维护两点之间是否连通的关系LCT可解决但是我想如果空间是够的话可持久化并查集也是可以写的。
//#include<bits/stdc++.h> #include<iostream> #include<iomanip> #include<cstdio> #include<cstring> #include<string> #include<vector> #include<queue> #include<deque> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<cctype> #include<utility> #include<map> #include<set> #include<bitset> #include<cmath> #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define INF 1000000000 #define ll long long #define db double #define RI register int using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { 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; } inline void put(int x) { x<0?putchar('-'),x=-x:0; int num=0;char ch[50]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); putchar('\n');return; } //LCT const int MAXN=10010; int n,m,top; int c[MAXN][2],f[MAXN],s[MAXN],r[MAXN]; char a[10]; inline int get(int x){return c[f[x]][1]==x||c[f[x]][0]==x;} inline void reveral(int x){int tmp=c[x][0];c[x][0]=c[x][1];c[x][1]=tmp;r[x]^=1;} inline void pushdown(int x) { if(r[x]) { if(c[x][0])reveral(c[x][0]); if(c[x][1])reveral(c[x][1]); r[x]=0;return; } } inline void rotate(int x) { int old=f[x],oldf=f[old],k=c[old][1]==x; c[old][k]=c[x][k^1];c[x][k^1]=old; if(get(old))c[oldf][c[oldf][1]==old]=x; if(c[old][k])f[c[old][k]]=old; f[old]=x;f[x]=oldf;return; } inline void splay(int x) { int y=x;top=0; s[++top]=y; while(get(y))s[++top]=y=f[y]; while(top)pushdown(s[top--]); while(get(x)) { int old=f[x],oldf=f[old]; if(get(old))rotate((c[old][1]==x)^(c[oldf][1]==old)?x:old); rotate(x); } return; } inline void access(int x) { for(int y=0;x;x=f[y=x]) splay(x),c[x][1]=y; } inline void make_root(int x) { access(x);splay(x); reveral(x); } inline int find_root(int x) { access(x);splay(x); while(c[x][0])pushdown(c[x][0]),x=c[x][0]; splay(x);return x; } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=m;++i) { scanf("%s",a+1); int x,y; x=read();y=read(); if(a[1]=='C') { make_root(x); f[x]=y; } if(a[1]=='D') { make_root(x); access(y);splay(y); c[y][0]=f[x]=0; } if(a[1]=='Q') { make_root(x); if(find_root(y)==x)puts("Yes"); else puts("No"); } } return 0; }
伸入理解LCT 我发现比较好玩,因为动态树一类的其解决的非常出色。
这还是一个动态的树的问题,有点难的是 点边转换 这个我着实是没有想到 对于一些dalao可能就是水题吧。
点边转换之后边权直接变成点权 不可行的是把指定当前边的任意一个点附带当前边的权值是不行的因为此树时动态的无论是查找还是pushup维护都是很难做到的。
还有一点是这道题的做法:求a+b最小值那么此时我们a排序 然后从小到大枚举a此时会有对应的b出现 这个答案一定是在比较小的a中出现+b 所以我们只需要维护一棵在a以内的b的最小生成树即可。
也就是说我们以每一个a都求出在这个a以内能用的b一棵最小的生成树求的是1~n的路径这并不妨碍我们的最小生成树计划。
//#include<bits/stdc++.h> #include<iostream> #include<iomanip> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<cctype> #include<queue> #include<deque> #include<vector> #include<utility> #include<cstdlib> #include<stack> #include<cmath> #include<ctime> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 1000000000 #define ll long long #define x(i) t[i].x #define y(i) t[i].y using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } inline void put(int x) { x<0?putchar('-'),x=-x:0; int num=0;char ch[50]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); putchar('\n');return; } const int MAXN=150010; int n,m,top,ans; int fa[MAXN],f[MAXN],c[MAXN][2],r[MAXN],b[MAXN]; int s[MAXN],mx[MAXN]; struct wy { int x,y; int a,b; int friend operator <(const wy w,const wy s) { return w.a<s.a; } }t[MAXN]; inline int getfather(int x){return x==fa[x]?x:fa[x]=getfather(fa[x]);} inline void reversal(int x){int tmp=c[x][0];c[x][0]=c[x][1];c[x][1]=tmp;r[x]^=1;return;} inline int get(int x){return c[f[x]][1]==x||c[f[x]][0]==x;} inline void pushup(int x) { mx[x]=x; if(b[mx[c[x][0]]]>b[mx[c[x][1]]]){if(b[mx[x]]<b[mx[c[x][0]]])mx[x]=mx[c[x][0]];} else if(b[mx[x]]<b[mx[c[x][1]]])mx[x]=mx[c[x][1]]; return; } inline void pushdown(int x) { if(r[x]) { if(c[x][0])reversal(c[x][0]); if(c[x][1])reversal(c[x][1]); r[x]=0; } return; } inline void rotate(int x) { int old=f[x],oldf=f[old],k=c[old][1]==x; c[old][k]=c[x][k^1];c[x][k^1]=old; if(c[old][k])f[c[old][k]]=old; if(get(old))c[oldf][c[oldf][1]==old]=x; f[x]=oldf;f[old]=x;pushup(old);return; } inline void splay(int x) { int y=x;top=0; s[++top]=y; while(get(y))s[++top]=y=f[y]; while(top)pushdown(s[top--]); while(get(x)) { int old=f[x],oldf=f[old]; if(get(old))rotate(((c[old][1]==x)^(c[oldf][1]==old))?x:old); rotate(x); } pushup(x);return; } inline void access(int x) { for(int y=0;x;x=f[y=x]) splay(x),c[x][1]=y,pushup(x); return; } inline void make_root(int x) { access(x);splay(x); reversal(x);return; } inline void contact(int x,int y) { make_root(x); f[x]=y;return; } inline int query(int x,int y) { make_root(x);access(y); splay(y);return mx[y]; } inline void cut(int x,int y) { make_root(x); access(y);splay(x);//注意再次splay //if(c[x][1]!=y)cout<<"wwww"<<endl; f[y]=c[x][1]=0; pushup(x); return; } int main() { //freopen("1.in","r",stdin); n=read();m=read();ans=INF; for(int i=1;i<=m;++i)t[i]=(wy){read(),read(),read(),read()}; sort(t+1,t+1+m); //for(int i=1;i<=m;++i)put(t[i].a); for(int i=1;i<=n+m;++i)fa[i]=i; for(int i=1;i<=m;++i)b[n+i]=t[i].b; for(int i=1;i<=m;++i) { if(x(i)==y(i)) { if(getfather(1)==getfather(n))ans=min(ans,t[i].a+b[query(1,n)]); continue;//80分的缘由 } int xx=getfather(x(i)); int yy=getfather(y(i)); if(xx!=yy) { contact(y(i),i+n); contact(x(i),i+n); fa[xx]=yy; } else { int id=query(x(i),y(i)); if(b[id]<=t[i].b) { if(getfather(1)==getfather(n))ans=min(ans,t[i].a+b[query(1,n)]); continue; } cut(t[id-n].x,id); cut(t[id-n].y,id); contact(x(i),i+n); contact(y(i),i+n); } if(t[i+1].a==t[i].a)continue; if(getfather(1)==getfather(n))ans=min(ans,t[i].a+b[query(1,n)]); } put(ans==INF?-1:ans); return 0; }
求最小差值生成树 怎么求呢 我们延伸上一题的思路对于每个权值我们构建出一棵<=当前权值的最大的生成树 当然是越大越好了,因为当前最大点是当前的点权。
这样不断加边维护树的形态即可。注意也还是加边。
//#include<bits/stdc++.h> #include<iostream> #include<iomanip> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<cctype> #include<queue> #include<deque> #include<vector> #include<utility> #include<cstdlib> #include<stack> #include<cmath> #include<ctime> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 1000000000 #define ll long long #define x(i) t[i].x #define y(i) t[i].y #define z(i) t[i].z #define IV inline void using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } inline void put(int x) { x<0?putchar('-'),x=-x:0; int num=0;char ch[50]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); putchar('\n');return; } const int MAXN=250010; int n,m,ans=INF,last,l,r,cnt,top; int fa[MAXN],b[MAXN],q[MAXN],s[MAXN],vis[MAXN]; int f[MAXN],c[MAXN][2],re[MAXN],mi[MAXN]; struct wy { int x,y; int z; int friend operator <(wy a,wy b){return a.z<b.z;} }t[MAXN]; inline int getfather(int x){return x==fa[x]?x:fa[x]=getfather(fa[x]);} inline int get(int x){return c[f[x]][1]==x||c[f[x]][0]==x;} inline void reversal(int x){int tmp=c[x][0];c[x][0]=c[x][1];c[x][1]=tmp;re[x]^=1;return;} IV pushup(int x) { mi[x]=x; if(b[mi[c[x][0]]]<b[mi[c[x][1]]]){if(b[mi[c[x][0]]]<b[mi[x]])mi[x]=mi[c[x][0]];} else if(b[mi[c[x][1]]]<b[mi[x]])mi[x]=mi[c[x][1]]; return; } IV pushdown(int x) { if(re[x]) { if(c[x][0])reversal(c[x][0]); if(c[x][1])reversal(c[x][1]); re[x]=0; } return; } IV rotate(int x) { int old=f[x],oldf=f[old],k=c[old][1]==x; c[old][k]=c[x][k^1];c[x][k^1]=old;//注意 if(get(old))c[oldf][c[oldf][1]==old]=x; if(c[old][k])f[c[old][k]]=old; f[x]=oldf;f[old]=x;pushup(old);return; } IV splay(int x) { int y=x;top=0; s[++top]=y; while(get(y))s[++top]=y=f[y]; while(top)pushdown(s[top--]); while(get(x)) { int old=f[x],oldf=f[old]; if(get(old))rotate(((c[old][1]==x)^(c[oldf][1]==old))?x:old); rotate(x); } pushup(x);return; } IV access(int x) { for(int y=0;x;x=f[y=x]) splay(x),c[x][1]=y,pushup(x); } IV make_root(int x) { access(x); splay(x); reversal(x);return; } IV contact(int x,int y) { make_root(x); f[x]=y;return; } inline int query(int x,int y) { make_root(x);access(y); splay(y);return mi[y]; } IV cut(int x,int y) { make_root(x);access(y); f[x]=c[y][0]=0;return; } int main() { //freopen("1.in","r",stdin); n=read();m=read();l=1;r=0; for(int i=1;i<=m;++i)t[i]=(wy){read(),read(),read()}; sort(t+1,t+1+m); for(int i=0;i<=n+m;++i)fa[i]=i,b[i]=(i<=n)?INF:z(i-n); for(int i=1;i<=m;++i) { if(x(i)==y(i))continue; int xx=getfather(x(i)); int yy=getfather(y(i)); if(xx!=yy) { fa[xx]=yy;++cnt;//f数组别打错 contact(x(i),i+n); contact(y(i),i+n); q[++r]=i; } else { int id=query(x(i),y(i)); vis[id-n]=1; cut(x(id-n),id); cut(y(id-n),id); contact(x(i),i+n); contact(y(i),i+n); q[++r]=i;while(vis[q[l]])++l; } if(cnt==n-1)ans=min(ans,z(q[r])-z(q[l])); } put(ans);return 0; }
这道题就很有意思了拿道题我思考20min还是不会写因为这个删除边我还要找一条边顶替上去如果我重新便利,显然我加边都不好加需要一些繁琐的操作找边->最小的->维护连通性。
非常麻烦仔细思考连通性这个地方 好像我让一个东西断开貌似有点不太顺因为寻找一条边加进去是非常ex的。
考虑一开始我都没有加进去这些边 而是使用其他边构成的这棵树 所以此时 我只需要_(:з」∠)_着加边即可。这不是上一条题么。
以后让删除什么的都不要相信 去反过来加进什么东西。
//#include<bits/stdc++.h> #include<iostream> #include<iomanip> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<cctype> #include<queue> #include<deque> #include<vector> #include<utility> #include<cstdlib> #include<stack> #include<cmath> #include<ctime> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 1000000000 #define ll long long using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } inline void put(int x) { x<0?putchar('-'),x=-x:0; int num=0;char ch[50]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); putchar('\n');return; } const int MAXN=101010; int n,m,Q,last,cnt,top; int fa[MAXN],b[MAXN],s[MAXN]; int f[MAXN],c[MAXN][2],re[MAXN],mx[MAXN]; int a[1001][1001],w[1001][1001]; struct wy { int x,y; int z; int friend operator <(wy a,wy b){return a.z<b.z;} }t[MAXN]; struct wy1 { int id,x,y; int ans; }q[MAXN]; inline int getfather(int x){return x==fa[x]?x:fa[x]=getfather(fa[x]);} inline int get(int x){return c[f[x]][1]==x||c[f[x]][0]==x;} inline void reversal(int x){int tmp=c[x][0];c[x][0]=c[x][1];c[x][1]=tmp;re[x]^=1;return;} inline void pushup(int x) { mx[x]=x; if(b[mx[c[x][0]]]>b[mx[c[x][1]]]){if(b[mx[x]]<b[mx[c[x][0]]])mx[x]=mx[c[x][0]];} else if(b[mx[x]]<b[mx[c[x][1]]])mx[x]=mx[c[x][1]]; return; } inline void pushdown(int x) { if(re[x]) { if(c[x][0])reversal(c[x][0]); if(c[x][1])reversal(c[x][1]); re[x]=0; } return; } inline void rotate(int x) { int old=f[x],oldf=f[old],k=c[old][1]==x; c[old][k]=c[x][k^1];c[x][k^1]=old;//注意 if(get(old))c[oldf][c[oldf][1]==old]=x; if(c[old][k])f[c[old][k]]=old; f[x]=oldf;f[old]=x;pushup(old);return; } inline void splay(int x) { int y=x;top=0; s[++top]=y; while(get(y))s[++top]=y=f[y]; while(top)pushdown(s[top--]); while(get(x)) { int old=f[x],oldf=f[old]; if(get(old))rotate(((c[old][1]==x)^(c[oldf][1]==old))?x:old); rotate(x); } pushup(x);return; } inline void access(int x) { for(int y=0;x;x=f[y=x]) splay(x),c[x][1]=y,pushup(x); } inline void make_root(int x) { access(x);splay(x); reversal(x);return; } inline void contact(int x,int y) { make_root(x); f[x]=y;return; } inline int query(int x,int y) { make_root(x);access(y); splay(y);return mx[y]; } inline void cut(int x,int y) { make_root(x);access(y); f[x]=c[y][0]=0;return; } int main() { //freopen("1.in","r",stdin); n=read();m=read();Q=read(); for(int i=1;i<=m;++i)t[i]=(wy){read(),read(),read()}; sort(t+1,t+1+m); for(int i=1;i<=n+m;++i) { fa[i]=i; if(i>n)b[i]=t[i-n].z,w[t[i-n].x][t[i-n].y]=w[t[i-n].y][t[i-n].x]=i; } for(int i=1;i<=Q;++i) { q[i]=(wy1){read(),read(),read()}; if(q[i].id==2)a[q[i].x][q[i].y]=a[q[i].y][q[i].x]=b[w[q[i].x][q[i].y]]; } for(int i=1;i<=m;++i) { if(a[t[i].x][t[i].y])continue; int xx=getfather(t[i].x); int yy=getfather(t[i].y); if(xx!=yy) { fa[xx]=yy;//f数组别打错 contact(t[i].x,i+n); contact(t[i].y,i+n); } } for(int i=Q;i>=1;--i) { if(q[i].id==1)q[i].ans=b[query(q[i].x,q[i].y)]; else { int xx=getfather(q[i].x); int yy=getfather(q[i].y); if(xx!=yy) { fa[xx]=yy; contact(q[i].x,w[q[i].x][q[i].y]); contact(q[i].y,w[q[i].x][q[i].y]); } else { int id=query(q[i].x,q[i].y); if(b[id]>a[q[i].x][q[i].y]) { cut(t[id-n].x,id); cut(t[id-n].y,id); contact(q[i].x,w[q[i].x][q[i].y]); contact(q[i].y,w[q[i].x][q[i].y]); } } } } for(int i=1;i<=Q;++i)if(q[i].id==1)put(q[i].ans); return 0; }
心情还是很烦躁,我必须要拿出我真正的实力了。