题目描述
有一个装球机器,构造可以看作是一棵树。有下面两种操作:
- 从根放入一个球,只要下方有空位,球会沿着树滚下。如果同时有多个点可以走,那么会选择编号最小的节点所在路径的方向。比如依次在树根
4
放2个球,第一个球会落到1
,第二个会落到3
:
- 从某个位置拿走一个球,那么它上方的球会落下来。比如依次拿走
5, 7, 8
三个球:
输入
第一行:球的个数N
,操作个数Q
(N, Q <= 100 000
)下面N
行:第i
个节点的父亲。如果是根,则为0
接下来Q
行:op num
op == 1
:在根放入num
个球op == 2
:拿走在位置num
的球
输出
保证输入合法
op == 1
:输出最后一个球落到了哪里op == 2
:输出拿走那个球后有多少个球会掉下来
样例输入
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
样例输出
1
3
2
2
3
2
2
这道题有两个操作,删球和加球,对于1操作,可以发现两个特性:1、对于一个节点,它下面的节点没填满,这个节点不会被填上。2、对于一个节点,它子树最小编号的子树没填满,其他子树不会被填球。那么就可以以子树最小编号的大小为顺序维护一个dfs序,在dfs序上,如果前面的位置没填满,后面的位置不会被填。如果因为删除而导致一段被填满的区间中有节点空缺,可以用小根堆(也可以优先队列)来每次取优先度最大的插入。删除操作就是删除一个节点上的球,然后这的节点到根节点链上所有球都向下落一层,也可以看作是把链上最上面有球的节点的球删掉,那么只要找到距离根最近的有球节点就行,但如果一层一层往上爬显然时间复杂度太高,所以需要倍增往上跳。
最后附上代码
1 #include<cstdio> 2 #include<algorithm> 3 #include<iostream> 4 #include<cmath> 5 #include<cstring> 6 #include<vector> 7 #include<queue> 8 using namespace std; 9 priority_queue< int,vector<int>,greater<int> >q; 10 vector<int>v[200010]; 11 int n,Q; 12 int f[20][200010];//倍增祖先 13 int m[200010];//子树最小的编号 14 int s[200010];//优先度 15 int r[200010];//优先度对应节点 16 int cnt; 17 int root; 18 int x,y; 19 int opt; 20 int d[200010];//深度 21 int vis[200010];//是否被填标记 22 bool cmp(int x,int y) 23 { 24 return m[x]<m[y]; 25 } 26 void dfs(int x)//按子树最小编号大小排序并处理深度 27 { 28 m[x]=x; 29 for(int i=0;i<v[x].size();i++) 30 { 31 f[0][v[x][i]]=x; 32 d[v[x][i]]=d[x]+1; 33 dfs(v[x][i]); 34 m[x]=min(m[x],m[v[x][i]]); 35 } 36 sort(&v[x][0],&v[x][v[x].size()],cmp); 37 } 38 void getsign(int x)//处理优先度 39 { 40 for(int i=0;i<v[x].size();i++) 41 { 42 getsign(v[x][i]); 43 } 44 s[x]=++cnt; 45 r[cnt]=x; 46 } 47 int main() 48 { 49 scanf("%d%d",&n,&Q); 50 for(int i=1;i<=n;i++) 51 { 52 scanf("%d",&x); 53 if(x==0) 54 { 55 root=i; 56 } 57 else 58 { 59 v[x].push_back(i); 60 } 61 } 62 for(int i=1;i<=n;i++) 63 { 64 q.push(i); 65 } 66 d[root]=1; 67 dfs(root); 68 getsign(root); 69 for(int i=1;i<=20;i++) 70 { 71 for(int j=1;j<=n;j++) 72 { 73 f[i][j]=f[i-1][f[i-1][j]]; 74 } 75 } 76 while(Q--) 77 { 78 scanf("%d%d",&opt,&x); 79 if(opt==1) 80 { 81 while(x--) 82 { 83 y=r[q.top()]; 84 q.pop(); 85 vis[y]=1; 86 } 87 printf("%d\n",y); 88 } 89 else 90 { 91 int fa=x; 92 for(int i=20;i>=0;i--) 93 { 94 if(vis[f[i][x]]==1) 95 { 96 x=f[i][x]; 97 } 98 } 99 vis[x]=0; 100 printf("%d\n",d[fa]-d[x]); 101 q.push(s[x]); 102 } 103 } 104 }