刷了那么久水题之后终于有一题可以来写写博客了。
但是这题太神仙了我还没完全弄懂……
upd:写完博客之后似乎懂了。
思路
首先很容易想到\(O(n^2\log n)\)乘上\(O(\frac{n}{\log n})\)的巨大常数的暴力做法(雾
然后可以发现这题支持把询问抽离出来最后做,那么我们可以先只关注修改操作。
可以发现一个点在\([l,r]\)的树上连上去和在所有树上都连上去其实没有太大区别,只是修改生长节点时要判一下是否存在,其他时候其实可以每一棵树上都连一个,因为不存在的点并不会被询问到。
发现这题主要恶心在1操作(也就是更换生长节点),如果没有1操作那么所有树的形态就都一样了。
考虑用某种方法使得我们可以只维护一棵树,然后在这一棵树上面实现从\(1\)到\(n\)的灵活转换。
其实也不用太过灵活,只需要能够从\(i\)快速推到\(i+1\)即可。
既然1操作恶心,那么我们重点分析1操作。
考虑\([l,r],x\)的这样一次操作,对\(l,l-1\)这两棵树会造成什么差异。假设\(l-1\)的生长节点为\(y\)。
那么这就相当于把接下来所有连在\(y\)上面的节点以及它们的子树全都接到\(x\)上。(注意:不管它们是否真的有在\(x\)树上,这对答案没有影响)
同理,\(r,r+1\)之间的差异相当于接回去。
那么我们只要迅速地修改一堆子树的父亲即可。
考虑在一个虚点下面连上这一堆子树,那么每次就只需要修改虚点的父亲就好了。
每当有一个新的1操作\([l,r],x\)时就新开一个虚点\(p\),表示接下来所有0操作的点都连到\(p\)上,同时\(p\)也要连到上一个虚点上,保证之前的虚点也连上了所有点。
\(p\)在\(l\)树之前一直连在原虚点上,\([l,r]\)时连在\(x\)上,之后再连回去。
然后就可以从\(1\)到\(n\)处理这棵树上的修改和询问,然后就做完了。
我不敢确定自己是否真的理解了做法,请发现思路上的错误的大佬指正,谢谢!
代码
注意这里没有保存一个点在原树上的父亲是谁,所以不能随便\(\text{makeroot}\)破坏LCT上的父子结构。
但是我还是不怎么明白\(\text{cut}\)函数中为什么\(lson(x)\)一定是\(x\)在原树中的父亲,希望有大佬赐教。
#include<bits/stdc++.h> clock_t t=clock(); namespace my_std{ using namespace std; #define pii pair<int,int> #define fir first #define sec second #define MP make_pair #define rep(i,x,y) for (int i=(x);i<=(y);i++) #define drep(i,x,y) for (int i=(x);i>=(y);i--) #define go(x) for (int i=head[x];i;i=edge[i].nxt) #define templ template<typename T> #define sz 404040 typedef long long ll; typedef double db; mt19937 rng(chrono::steady_clock::now().time_since_epoch().count()); templ inline T rnd(T l,T r) {return uniform_int_distribution<T>(l,r)(rng);} templ inline bool chkmax(T &x,T y){return x<y?x=y,1:0;} templ inline bool chkmin(T &x,T y){return x>y?x=y,1:0;} templ inline void read(T& t) { t=0;char f=0,ch=getchar();double d=0.1; while(ch>'9'||ch<'0') f|=(ch=='-'),ch=getchar(); while(ch<='9'&&ch>='0') t=t*10+ch-48,ch=getchar(); if(ch=='.'){ch=getchar();while(ch<='9'&&ch>='0') t+=d*(ch^48),d*=0.1,ch=getchar();} t=(f?-t:t); } template<typename T,typename... Args>inline void read(T& t,Args&... args){read(t); read(args...);} char __sr[1<<21],__z[20];int __C=-1,__zz=0; inline void Ot(){fwrite(__sr,1,__C+1,stdout),__C=-1;} inline void print(register int x) { if(__C>1<<20)Ot();if(x<0)__sr[++__C]='-',x=-x; while(__z[++__zz]=x%10+48,x/=10); while(__sr[++__C]=__z[__zz],--__zz);__sr[++__C]='\n'; } void file() { #ifndef ONLINE_JUDGE freopen("a.in","r",stdin); #endif } inline void chktime() { #ifndef ONLINE_JUDGE cout<<(clock()-t)/1000.0<<'\n'; #endif } #ifdef mod ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x%mod) if (y&1) ret=ret*x%mod;return ret;} ll inv(ll x){return ksm(x,mod-2);} #else ll ksm(ll x,int y){ll ret=1;for (;y;y>>=1,x=x*x) if (y&1) ret=ret*x;return ret;} #endif // inline ll mul(ll a,ll b){ll d=(ll)(a*(double)b/mod+0.5);ll ret=a*b-d*mod;if (ret<0) ret+=mod;return ret;} } using namespace my_std; int n,m; int fa[sz],ch[sz][2],w[sz],sum[sz]; #define ls ch[x][0] #define rs ch[x][1] void pushup(int x){sum[x]=w[x]+sum[ls]+sum[rs];} bool get(int x){return ch[fa[x]][1]==x;} bool nroot(int x){return ch[fa[x]][0]==x||ch[fa[x]][1]==x;} void rotate(int x) { int y=fa[x],z=fa[y],k=get(x),w=ch[x][!k]; if (nroot(y)) ch[z][get(y)]=x;ch[x][!k]=y;ch[y][k]=w; if (w) fa[w]=y;fa[y]=x;fa[x]=z; pushup(y);pushup(x); } void splay(int x){for (int y;(y=fa[x],nroot(x));rotate(x)) if (nroot(y)) rotate(get(x)==get(y)?y:x);} int access(int x){int y=0;for (;x;x=fa[y=x]) splay(x),rs=y,pushup(x);return y;} void link(int x,int y){splay(x);fa[x]=y;} void cut(int x){access(x);splay(x);fa[ls]=0;ls=0;pushup(x);} int query(int x,int y) { int ret=0; access(x);splay(x);ret+=sum[x]; int lca=access(y); splay(y); ret+=sum[y]; access(lca);splay(lca);ret-=2*sum[lca]; return ret; } int L[sz],R[sz]; // x点存在的区间 int pos[sz]; // x在LCT中的编号 struct hh { int pos,x,y,id; const bool operator < (const hh &y) const { return pos==y.pos?id<y.id:pos<y.pos; } }q[sz];int c; int ans[sz]; int main() { file(); read(n,m); int ima,real,cnt; // 当前虚点、实点编号,总编号 ima=2;real=1;cnt=2; L[1]=1;R[1]=n;pos[1]=1;w[1]=sum[1]=1; link(2,1); int opt,x,y,z,l,r; rep(i,1,m) { read(opt); if (opt==0) { read(l,r); x=++real;pos[x]=++cnt;w[pos[x]]=sum[pos[x]]=1; link(pos[x],ima); L[x]=l,R[x]=r; } else if (opt==1) { read(l,r,x); l=max(l,L[x]);r=min(r,R[x]); if (l>r) continue; z=++cnt;link(z,ima); q[++c]=(hh){l,z,pos[x],i-m}; q[++c]=(hh){r+1,z,ima,i-m}; ima=z; } else read(z,x,y),q[++c]=(hh){z,pos[x],pos[y],i}; } sort(q+1,q+c+1); memset(ans,-1,sizeof(ans)); rep(i,1,c) if (q[i].id<=0) cut(q[i].x),link(q[i].x,q[i].y); else ans[q[i].id]=query(q[i].x,q[i].y); rep(i,1,m) if (ans[i]!=-1) printf("%d\n",ans[i]); return 0; }
来源:https://www.cnblogs.com/p-b-p-b/p/10660155.html