目录

[BZOJ 4573][ZJOI 2016]大♂森林

迁移提示
本文迁移自博客园, 原文链接

[LOJ 2092][BZOJ 4573][UOJ 195][ZJOI 2016]大♂森林

题意

给定一个树序列, 初始时所有树都只有一个点, 要求支持三种操作:

  1. 区间种树(在某个特定点上长出一个子结点)
  2. 区间更改种树点(就是改上面那个操作中的「特定点」)
  3. 查询某棵树上两个点间的距离

$n\le 1\times 10^5, q\le 2\times 10^5$. 不强制在线. 长出来的点标号一致, 与种树操作的顺序一致. 保证2操作合法.

题解

ZJOI都是神仙题啊QAQ…

首先这题序列上的元素是树…一棵树得占它个 $O(n)$ 级别的空间所以肯定得想办法离线之后一个一个处理…

其次我们发现好像对于已经长出来的点, 后面再怎么修改也不会和它有什么关系了. 于是我们可以把重点放在前两个操作而把 2 操作丢到最后等树形态构造完成之后再搞.

还有一点就是其实多长出来的点并不会影响答案(因为保证 2 操作合法, 而不合法的点一定不会是合法的点的祖先), 所以其实可以考虑维护一整棵包含所有 0 操作生长出来的结点的树. 这样我们就可以避免一直添点删点的问题了.

也就是说我们现在只剩下最辣手的操作 1 了.

考虑从第 $i$ 棵树的形态如何快速转化为第 $i+1$ 棵的形态.

如果这两棵树形态不同, 必然是一棵执行了某个操作 1 而另一个没有. 假设原生长点是 $u$, 新生长点是 $v$, 则在操作 1 之后, 原来长在 $u$ 下面的所有点实际上都应该挂在 $v$ 下面才对. 所以实际上就是一个子树移动的过程.

子树移动? Cut一下再Link一下就完了?

假的…

现在的需求是将某个点下面的所有子树都移动走, 但是它有多少子树可不一定…

我们可以放一个虚点来把它们收束起来. 预处理的时候对每个 1 操作建立一个虚点, 距离这个操作最近的挂上去的点都挂在这个虚点上. 这样如果这个 1 操作产生了子树移动, 直接把虚点切下来就好了.

什么你说虚点会导致距离增加? 裆燃是把虚点的 size 设为 0 辣~

一些小的注意事项

  1. 注意LCT查询LCA的正确姿势以及不同Access写法的不同副作用. ($0\text{pts}\rightarrow40\text{pts}$)
  2. 注意每个实点都有一个存在区间, 如果要把某个子树挂到 $v$ 上需要检查 $v$ 在哪一段中存在. 不存在的不能理会. ($40\text{pts}\rightarrow70\text{pts}$)
  3. 注意 1 号点的存在区间要初始化为 $[1,n]$. ($70\text{pts}\rightarrow100\text{pts}$)

参考代码

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
#include <bits/stdc++.h>
#define _O0 __attribute__((optimize("O0"))) // 防止沙雕编译器把this当成非空来优化

const int MAXN=3e5+10;

struct Query{
	int type;
	int time;
	int u;
	int v;
	int r;
	bool friend operator<(const Query& a,const Query& b){
		return std::make_pair(a.type,a.time)<std::make_pair(b.type,b.time);
	}
};

struct Dump{
	int t;
	int a;
	int b;
	int c;
};

#define lch chd[0]
#define rch chd[1]
#define kch chd[k]
#define xch chd[k^1]

struct Node{
	int v;
	int sz;
	bool rev;
	Node* prt;
	Node* pprt;
	Node* chd[2];
	Node(int v):v(v),sz(v),rev(false),prt(NULL),pprt(NULL),chd{NULL,NULL}{}
	inline _O0 int size(){
		return this==NULL?0:this->sz;
	}
	inline void Maintain(){
		this->sz=this->lch->size()+this->rch->size()+this->v;
	}
	inline _O0 void Flip(){
		if(this!=NULL){
			std::swap(this->lch,this->rch);
			this->rev=!this->rev;
		}
	}
	inline void PushDown(){
		if(this->rev){
			this->lch->Flip();
			this->rch->Flip();
			this->rev=false;
		}
	}
};
Node* N[MAXN];

void Rotate(Node* root,int k){
	Node* tmp=root->xch;
	root->PushDown();
	tmp->PushDown();
	tmp->prt=root->prt;
	if(root->prt==NULL){
		tmp->pprt=root->pprt;
		root->pprt=NULL;
	}
	else if(root->prt->lch==root)
		root->prt->lch=tmp;
	else
		root->prt->rch=tmp;
	root->xch=tmp->kch;
	if(root->xch!=NULL)
		root->xch->prt=root;
	tmp->kch=root;
	root->prt=tmp;
	root->Maintain();
	tmp->Maintain();
}

void Splay(Node* root){
	while(root->prt!=NULL){
		int k=root->prt->lch==root;
		if(root->prt->prt==NULL)
			Rotate(root->prt,k);
		else{
			int d=root->prt->prt->lch==root->prt;
			Rotate(k==d?root->prt->prt:root->prt,k);
			Rotate(root->prt,d);
		}
	}
}

void Expose(Node* root){
	Splay(root);
	root->PushDown();
	if(root->rch){
		root->rch->pprt=root;
		root->rch->prt=NULL;
		root->rch=NULL;
		root->Maintain();
	}
}

Node* Access(Node* root){
	Node* dump=root;
	Node* tmp=NULL;
	while(root){
		Expose(root);
		root->rch=tmp;
		if(tmp){
			tmp->pprt=NULL;
			tmp->prt=root;
		}
		root->Maintain();
		tmp=root;
		root=root->pprt;
	}
	Splay(dump);
	return tmp;
}

void Evert(Node* root){
	Access(root);
	root->Flip();
}

Node* FindRoot(Node* root){
	Access(root);
	Node* ans=root;
	while(ans->lch)
		ans=ans->lch;
	Splay(ans);
	return ans;
}

int Calc(int a,int b){
	if(FindRoot(N[a])!=FindRoot(N[b]))
		return -1;
	int ans=0;
	Access(N[a]);
	ans+=N[a]->size();
	Node* lca=Access(N[b]);
	ans+=N[b]->size();
//	printf("%d\n",ans);
	Access(lca);
	ans-=2*lca->size();
//	printf("%d %d lca=%p\n",a,b,lca);
	return ans;
}

void Link(int x,int y){
//	printf("link %d <- %d\n",x,y);
	Evert(N[y]);
	N[y]->pprt=N[x];
}

void Cut(int x){
//	printf("cut %d\n",x);
	Evert(N[1]);
	Access(N[x]);
	N[x]->PushDown();
	N[x]->lch->prt=NULL;
	N[x]->lch=NULL;
	N[x]->Maintain();
}

int n;
int q;
int cnt;
int L[MAXN];
int R[MAXN];
Dump D[MAXN];
int ans[MAXN];
int prt[MAXN];
int alive[MAXN];
std::vector<Query> Q[MAXN];

int main(){
	scanf("%d%d",&n,&q);
	cnt=1;
	N[0]=new Node(0);
	N[1]=new Node(1);
	Link(1,0);
	L[1]=1,R[1]=n;
	prt[0]=1;
	prt[1]=-1;
	for(int i=0;i<q;i++){
		scanf("%d%d%d",&D[i].t,&D[i].a,&D[i].b);
		if(D[i].t!=0)
			scanf("%d",&D[i].c);
		else{
			N[++cnt]=new Node(1);
			L[cnt]=D[i].a;
			R[cnt]=D[i].b;
		}
	}
	int last=0;
	int cur=1;
	for(int i=0;i<q;i++){
		if(D[i].t==0){
			prt[++cur]=last;
			Link(last,cur);
		}
		else if(D[i].t==1){
			int l=std::max(L[D[i].c],D[i].a),r=std::min(R[D[i].c],D[i].b);
			if(l>r)
				continue;
			N[++cnt]=new Node(0);
			Link(last,cnt);
			prt[cnt]=last;
			last=cnt;
			Q[l].push_back({1,i,cnt,D[i].c,r+1});
		}
		else
			Q[D[i].a].push_back({2,i,D[i].b,D[i].c,0});
	}
//	for(int i=0;i<=cnt;i++)
//		printf("N[%d]=%p\n",i,N[i]);
	for(int i=1;i<=n;i++){
		std::sort(Q[i].begin(),Q[i].end());
		for(auto q:Q[i]){
			if(q.type==1){
				if(q.r)
					Q[q.r].push_back({1,q.time,q.u,prt[q.u],0});
				Cut(q.u);
				Link(q.v,q.u);
				prt[q.u]=q.v;
			}
			else{
			//	assert(q.type==2);
				ans[q.time]=Calc(q.u,q.v);
			}
		}
	}
	for(int i=0;i<q;i++)
		if(D[i].t==2)
			printf("%d\n",ans[i]);
	return 0;
}

https://pic.rvalue.moe/2021/08/02/fc28a8040024c.png