题目描述
给定一棵以 1 为根,大小为 N 的树,每个点有若干条出边,如果你要
dfs 的话,它们需要按照输入的顺序去遍历(就是指定了dfs 序)
你需要维护一种数据结构,支持两种操作:
1 x:将 x 为根的子树中的节点,以dfs 序为下标,编号大小为关键字排序。相当于树的形态不变,但是子树里节点的编号排了遍序,dfs 序小的对应编号小的,dfs 序大的对应编号大的
2 x:查询原先树中点 x 现在的编号是多少
一共会给出 Q 个询问。 (N,Q <= 1e5)
题解:
操作1是对子树排序,相当于缩点,缩在一起的结点是排好序的。先处理出每个点的dfs序号,每次查询u,如果u没有缩过点,说明u的任一父节点都没有进行过操作也,也就是说u的位置没有变化,直接输出u。如果u缩过点,找到和u在同一个集合内深度最小的点,这个点为根的子树全部排好序了。k为u原先在这个子树上是第几个dfs到的,主席树查询子树上第k大就得到了排完序之后该结点上的编号。
AC代码:
#include<bits/stdc++.h>
#define mid ((l+r)>>1)
using namespace std;
const int maxn = 1e5 + 50;
vector<int> g[maxn];
int dfn[maxn];
int id[maxn];
int sz[maxn];
int T[maxn*20],ls[maxn*20],rs[maxn*20],s[maxn*20];
int dex;
int n,q;
void dfs(int u){
dfn[++dex] = u, id[u] = dex, sz[u] = 1;//出过的bug:++dex写成dex++
for(int i = 0;i < g[u].size();++i) {
dfs(g[u][i]);sz[u]+=sz[g[u][i]];
}
}
void init(){
scanf("%d%d",&n,&q);
for(int u = 1;u <= n;++u){
int k;scanf("%d",&k);
while(k--){
int v;scanf("%d",&v);
g[u].push_back(v);
}
}
dex = 0;
dfs(1);
}
int tot;
void build(int pre,int cur,int l,int r,int pos){
s[cur] = s[pre] + 1, ls[cur] = ls[pre], rs[cur] = rs[pre];
if(l == r) return;
if(pos <= mid)
build(ls[pre], ls[cur] = ++tot, l, mid, pos);
else
build(rs[pre], rs[cur] = ++tot, mid+1, r, pos);
}
int query(int pre,int cur,int l,int r,int k){
if(l == r) return l;
int res = s[ls[cur]] - s[ls[pre]];
if(res >= k)
return query(ls[pre], ls[cur], l, mid, k);
else
return query(rs[pre], rs[cur], mid + 1, r, k - res);
}
int fa[maxn];
int fnd(int x){if(fa[x] == x) return x;return fa[x] = fnd(fa[x]);}
void modify(int u,int acc){
if(fa[u]){
fa[fnd(u)] = acc;return;
}
fa[u] = acc;
for(int i = 0;i < g[u].size();++i) modify(g[u][i],acc);
}
int query(int u){
if(!fa[u]) return u;//没被缩过,编号没有变化
int f = fnd(u);
return query(T[id[f] - 1],T[id[f] + sz[f] - 1],1,n,id[u] - id[f] + 1);
}
void sol(){
tot = 0;
for(int i = 1;i <= n;++i) {
build(T[i-1],T[i] = ++tot,1,n,dfn[i]);
}
while(q--){
int op,x;scanf("%d%d",&op,&x);
if(op == 1){
if(fa[x]) continue;//之前缩过点,则已经有序
modify(x,x);
}
else{
printf("%d\n",query(x));
}
}
}
int main(){
init();sol();
}