LG8204 [Ynoi2005] tdnmo 【树分块,莫队】

定义树的是一个连通子树,使得该连通子树中至多有两个点与外部相连。与外部相连的点称为界点。若一个簇有两个界点则这两点间的路径称为簇路径。为了方便也把整棵树的根视为界点,而要求簇路径是祖先-后代关系的。

Top Cluster 将 \(n\) 个点的树划分为 \(\mathcal O(n/B)\) 个大小 \(\mathcal O(B)\) 的簇,构造如下:

  • dfs 这棵树,维护栈存储当前不属于任何一个簇的边;
  • 若栈中当前子树的边数 \(>B\) 或有两个界点,就把当前点设为界点,栈中在当前子树的边作为一个簇。

然后直接回滚莫队就可以了:对于询问的邻域 \(\mathcal N(x_i,y_i)\),若在一块内部则直接从空集转过来,否则 \(x_i\) 一定在簇路径上,对所在簇路径相同的询问排序即可。

#include<bits/stdc++.h>
#define MP make_pair
#define fi first
#define se second
using namespace std;
typedef pair<int, bool> pib;
typedef tuple<int, int, int> ti3;
const int N = 1000003, B = 1024;
int n, m, cnt, fa[N], dep[N], hd[N], to[N], nxt[N], son[N], qct, tp;
bool spe[N];
void add(int u, int v){to[++cnt] = v; nxt[cnt] = hd[u]; hd[u] = cnt;}
vector<ti3> qry[N];
pib dfs(int x){
	int siz = 1, flg = 0;
	for(int i = hd[x];i;i = nxt[i]){
		int v = to[i]; pib tmp = dfs(v);
		siz += tmp.fi; flg += tmp.se;
	}
	if(siz >= B || flg >= 2){siz = 0; spe[x] = true;}
	return MP(siz, flg || spe[x]);
}
char ot[1 << 26], *ol = ot;
void pc(int c){*ol++ = c;}
void write(int x){if(x >= 10) write(x / 10); pc(x % 10 + '0');}
void Add(int x, int y){++ qct; ++ tp; pc('1'); pc(' '); write(x); pc(' '); write(y); pc('\n');}
void Undo(){++ qct; -- tp; pc('2'); pc('\n');}
void Answer(int id){++ qct; pc('3'); pc(' '); write(id); pc('\n');}
int main(){
	ios::sync_with_stdio(0);
	cin >> n >> m;
	for(int i = 2;i <= n;++ i){
		cin >> fa[i]; add(fa[i], i);
		dep[i] = dep[fa[i]] + 1;
	}
	dfs(1);
	for(int i = 1;i <= n;++ i) if(spe[i])
		for(int j = i;j && !son[j];j = fa[j]) son[j] = i;
	for(int i = 1, x, y;i <= m;++ i){
		cin >> x >> y;
		if(!y) Answer(i);
		else if(!son[x]){Add(x, y); Answer(i); Undo();}
		else qry[son[x]].emplace_back(x, y, i);
	}
	for(int i = 1;i <= n;++ i) if(spe[i] && !qry[i].empty()){
		sort(qry[i].begin(), qry[i].end(), [&](const ti3 &a, const ti3 &b){
			int d1 = dep[get<0>(a)] + get<1>(a), d2 = dep[get<0>(b)] + get<1>(b);
			return d1 == d2 ? get<0>(a) > get<0>(b) : d1 < d2;
		});
		while(tp) Undo();
		for(auto [x, y, id] : qry[i]){
			Add(i, max(0, dep[x] + y - dep[i]));
			Add(x, y); Answer(id); Undo();
		}
	}
	printf("%d\n", qct);
	fwrite(ot, 1, ol - ot, stdout);
}
posted @ 2022-07-21 11:02  mizu164  阅读(101)  评论(0)    收藏  举报