BZOJ_3133_[Baltic2013]ballmachine_堆+倍增
Description
有一个装球机器,构造可以看作是一棵树。有下面两种操作:
- 从根放入一个球,只要下方有空位,球会沿着树滚下。如果同时有多个点可以走,那么会选择编号最小的节点所在路径的方向。比如依次在树根
4
放2个球,第一个球会落到1
,第二个会落到3
:
- 从某个位置拿走一个球,那么它上方的球会落下来。比如依次拿走
5, 7, 8
三个球:
Input
第一行:球的个数N
,操作个数Q
(N, Q <= 100 000
)下面N
行:第i
个节点的父亲。如果是根,则为0
接下来Q
行:op num
op == 1
:在根放入num
个球op == 2
:拿走在位置num
的球
Output
保证输入合法
op == 1
:输出最后一个球落到了哪里op == 2
:输出拿走那个球后有多少个球会掉下来
Sample Input
8 4
0
1
2
2
3
3
4
6
1 8
2 5
2 7
2 8
0
1
2
2
3
3
4
6
1 8
2 5
2 7
2 8
Sample Output
1
3
2
2
可以发现球的位置顺序是固定的,预处理出来每个点应该被放入小球的优先度,然后这个可以把边表排序之后dfs一遍求出。
用一个堆来维护当前没被放入的球的编号。
删除时倍增祖先,找到第一个没被放球的位置即可。
代码:
#include <cstdio> #include <string.h> #include <algorithm> #include <vector> #include <queue> using namespace std; #define N 200050 priority_queue<int>q; vector <int> v[N]; int n,T,mn[N],dfn[N],idx[N],tdfn[N],tidx[N],c[N],vis[N],f[21][N],dep[N],rt,son[N]; bool cmp(int x,int y) { return mn[x]<mn[y]; } void dfs(int x) { int i; int t=v[x].size(); mn[x]=x; for(i=0;i<t;i++) { f[0][v[x][i]]=x; dep[v[x][i]]=dep[x]+1; dfs(v[x][i]); mn[x]=min(mn[x],mn[v[x][i]]); } sort(&v[x][0],&v[x][t],cmp); } void solve(int x) { int i,t=v[x].size(); for(i=0;i<t;i++) { solve(v[x][i]); } idx[x]=++idx[0]; tidx[idx[0]]=x; } int main() { scanf("%d%d",&n,&T); int i,x,y,j; for(i=1;i<=n;i++) { scanf("%d",&x); if(!x) rt=i; else v[x].push_back(i); } for(i=1;i<=n;i++) { q.push(-i); } dep[rt]=1; dfs(rt); solve(rt); for(i=1;(1<<i)<=n;i++) { for(j=1;j<=n;j++) { f[i][j]=f[i-1][f[i-1][j]]; } } int opt; while(T--) { scanf("%d%d",&opt,&x); if(opt==1) { while(x--) { y=tidx[-q.top()]; q.pop(); vis[y]=1; } printf("%d ",y); }else { int t=x; for(i=20;i>=0;i--) { if(vis[f[i][x]]) x=f[i][x]; } vis[x]=0; printf("%d ",dep[t]-dep[x]); q.push(-idx[x]); } } }