bzoj 4573: [Zjoi2016]大森林 lct splay
http://www.lydsy.com/JudgeOnline/problem.php?id=4573
http://blog.csdn.net/lych_cys/article/details/53515748#
lct的难点大概是转换的部分。
这道题需建两种权值不同的点,每次更换生长节点建立权值为0的虚点,生长新点建立权值为1的实点,因为最开始有一个带权值的生长节点,那么建一个虚点一个实点来表示。
因为求的东西追根溯源只是链,每棵树从根到某点的链的结构都是交错的所以可以这样压缩点以及点点之间的关系。
实点需要连向最近建立的虚点,虚点需要连向最近建立的实点,注意一下离线前虚点被连成一条链,离线操作时某一个控制范围结束也是将这个虚点连回原来的链上。
按照操作的树的起始位置和操作时间排序,就可以离线处理了。这个离线操作大概可以视为几个实点虚点交错的链两端断开连接。
计算距离lca实现,两次access+splay计算的两个点x和y到根的距离和,他们的lca其实是 access y 的时候的最后一个连通块的入点,这个应该想一下可以想到。
access lca的时候我才发现access是删掉整个右子树的,这样操作顺利删掉了y到lca的这段距离。(原来以前都没有意识到access的原理么我果然是个傻子)。
注释什么的代码里解释的很详细了。
过了157个人,时间空间排名83竟然有点开心(还有74个人想的板子没有我抄的板子快呀(有毒))。
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 const int maxn=200010; 8 int n,m; 9 int ch[maxn][2]={},fa[maxn]={},siz[maxn]={};//splay相关 10 int sum[maxn]={},num[maxn]={};//splay子树和,点值(0/1) 11 int l[maxn]={},r[maxn]={};//对实点的记录,每个实点存在的树的标号范围 12 int b[maxn]={};//编号为i的实点的编号为b[i] 13 int tly=0,cnt=0;//tly是对点数的记录,cnt表示当前生成到编号为cnt的实点 14 int ans[maxn]={}; 15 struct nod{ 16 int z,ti,x,y;//操作的树的序号 操作时间(排序的一个依据) 需要连的两个点 17 }a[maxn*2];int tot=0; 18 bool mcmp(nod aa,nod bb){ return aa.z<bb.z||(aa.z==bb.z&&aa.ti<bb.ti); } 19 //离线操作是先按位置后按地点的方式排列的。 20 inline bool isroot(int x){ return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;} 21 inline void updata(int x){ sum[x]=sum[ch[x][1]]+sum[ch[x][0]]+num[x];} 22 inline void newp(int v){ tly++;num[tly]=sum[tly]=v; } 23 inline void init(int z,int ti,int x,int y){ 24 a[++tot].z=z;a[tot].ti=ti;a[tot].x=x;a[tot].y=y; 25 } 26 void rotate(int x){ 27 int y=fa[x];int fy=fa[y]; 28 int l=ch[y][0]==x?0:1;int r=l^1; 29 if(!isroot(y)){ 30 if(ch[fy][1]==y) ch[fy][1]=x; 31 else ch[fy][0]=x; 32 } 33 fa[ch[x][r]]=y; fa[x]=fy; fa[y]=x; 34 ch[y][l]=ch[x][r]; ch[x][r]=y; 35 updata(y); 36 } 37 void splay(int x){ 38 while(!isroot(x)){ 39 int y=fa[x];int fy=fa[y]; 40 if(!isroot(y)){ 41 if((ch[y][0]==x)^(ch[fy][0]==y)) rotate(x); 42 else rotate(y); 43 } 44 rotate(x); 45 }updata(x); 46 } 47 int access(int x){ 48 int y=0; 49 while(x){ 50 splay(x); 51 ch[x][1]=y; 52 updata(x); 53 y=x;x=fa[x];//这里的y和上面的含义不同,这里的是儿子。 54 }return y; 55 } 56 void cut(int x){ 57 access(x);splay(x); 58 fa[ch[x][0]]=0;ch[x][0]=0;updata(x); 59 } 60 void link(int x,int y){ cut(x);fa[x]=y;} 61 int main(){ 62 scanf("%d%d",&n,&m); 63 newp(1);cnt=1;b[1]=l[1]=1;r[1]=n; 64 newp(0);fa[2]=1; 65 int x,y,z,f;int now=2; 66 for(int i=1;i<=m;i++){ 67 scanf("%d",&f); 68 if(!f){ 69 cnt++; 70 scanf("%d%d",&l[cnt],&r[cnt]); 71 newp(1);b[cnt]=tly; 72 init(1,i-m,tly,now);//操作的树的序号 时间(排序的两个依据) 需要连的两个点 73 } 74 else if(f==1){ 75 scanf("%d%d%d",&x,&y,&z); 76 x=max(x,l[z]);y=min(y,r[z]); 77 if(x<=y){ 78 newp(0); 79 if(x>1)fa[tly]=now; 80 init(x,i-m,tly,b[z]); 81 init(y+1,i-m,tly,now); 82 now=tly; 83 } 84 } 85 else{ 86 scanf("%d%d%d",&z,&x,&y); 87 init(z,i,b[x],b[y]); 88 } 89 } 90 sort(a+1,a+1+tot,mcmp); 91 memset(ans,-1,sizeof(ans)); 92 int j=1; 93 for(int i=1;i<=n;i++){ 94 for(;j<=tot&&a[j].z==i;j++){ 95 if(a[j].ti>0){ 96 access(a[j].x);splay(a[j].x); 97 ans[a[j].ti]=sum[a[j].x]; 98 x=access(a[j].y); 99 splay(a[j].y); 100 ans[a[j].ti]+=sum[a[j].y]; 101 access(x);splay(x); 102 ans[a[j].ti]-=sum[x]*2; 103 104 } 105 else{ 106 link(a[j].x,a[j].y); 107 } 108 } 109 }for(int i=1;i<=m;i++)if(ans[i]!=-1)printf("%d\n",ans[i]); 110 return 0; 111 }