P6753 [BalticOI 2013 Day1] Ball Machine 题解
Solution
一道水紫
首先考虑放球操作
观察到小球每次下落必然选择子树内编号最小的点所在的子树下落,所以我们可以先预处理出
之后将每个结点的出边按照
接着考虑撤球操作,如果一个球被撤掉了,那么从这个节点到根的这条链上所有的球都会向下一个,所以可以倍增找到这条链上第一个没有球的结点,深度之差即为答案。
对于放球操作,每放一个球时间复杂度为
Code
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
int n,Q,rt;
int vis[N];
int dfn[N],rnk[N],cnt;
int dep[N],f[N][18];
int Min[N];
vector<int> vec[N];
priority_queue<int,vector<int>,greater<int> > q;
bool cmp(int x,int y){
return Min[x]<Min[y];
}
void dfs1(int u){
Min[u]=u,dep[u]=dep[f[u][0]]+1;
for(int i=1;i<=17;i++) f[u][i]=f[f[u][i-1]][i-1];
for(int i=0;i<vec[u].size();i++){
int v=vec[u][i];
dfs1(v);
Min[u]=min(Min[u],Min[v]);
}
}
void dfs2(int u){
for(int i=0;i<vec[u].size();i++){
int v=vec[u][i];
dfs2(v);
}
dfn[u]=++cnt,rnk[cnt]=u;
}
int main(){
//freopen(".in","r",stdin);
//freopen(".out","w",stdout);
scanf("%d%d",&n,&Q);
for(int i=1;i<=n;i++){
scanf("%d",&f[i][0]);
if(!f[i][0]) rt=i;
else vec[f[i][0]].push_back(i);
}
dfs1(rt);
for(int i=1;i<=n;i++) sort(vec[i].begin(),vec[i].end(),cmp);
dfs2(rt);
for(int i=1;i<=n;i++) q.push(i);
while(Q--){
int op,x;
scanf("%d%d",&op,&x);
if(op==1){
int ans=0;
for(int i=1;i<=x;i++){
ans=q.top();
q.pop();
vis[rnk[ans]]=1;
}
printf("%d\n",rnk[ans]);
}
else{
int ans=0,y=x;
for(int i=17;~i;i--) if(vis[f[y][i]]) y=f[y][i];
vis[y]=0;
q.push(dfn[y]);
printf("%d\n",dep[x]-dep[y]);
}
}
return 0;
}