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);
}