題目鏈接:傳送門
題目解析
考慮樹中沒有球的情況:
我們一個個把球放進去,共放個球,把第個節點的球是第幾個球記爲。
比如樣例:
把個球放進去,個球分別落到,稱這個排列爲掉落順序。
不難發現若掉落順序靠前的點爲空(即沒有球),則新的球不珂能掉到掉落順序靠後的點。
舉例:若爲空,那麼新的球不珂能掉落到。
所以一個新的球一定會落到爲空的點中掉落順序最靠前的點qwq。
所以對於操作1,建一個以值爲關鍵字的小根堆(優先隊列),存儲所有非空的點,新加入一個球就把堆頂取出來,打上標記表示這裏有球即可qwq。
在根放入個球就暴力放次即珂。
操作2更簡單,易證把一個球刪掉就相當於它到根上有球的深度最淺的節點的球刪掉。
因爲如果一個節點沒有球,那它的祖先也沒有球(不然就掉下來了qwq)
所以用倍增(樹剖珂能珂以?沒試過qwq)求出要刪除的節點到根的最淺的有球的節點,把這個節點的標記去掉,再把這個節點加入堆。
複雜度分析
空間複雜度(anc數組(倍增數組)要開)
時間複雜度:
預處理掉落順序:最壞情況應該是菊花圖,複雜度
插入球和刪除球都是(對堆操作和倍增都是)
使用攤還分析,發現一次刪除會使最多插入的球數+1,所以刪除的攤還代價爲
總共次,所以時間複雜度
總時間複雜度 qwq
毒瘤代碼
#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define re register int
#define rl register ll
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=(x<<1)+(x<<3)+ch-'0';
ch=getchar();
}
return x*f;
}
inline void write(const int x) {
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int Size=200005;
int n,q,cnt,head[Size];
struct Edge {
int v,next;
} w[Size<<1];
void AddEdge(int u,int v) {
w[++cnt].v=v;
w[cnt].next=head[u];
head[u]=cnt;
}
int deep[Size],anc[Size][21],minv[Size];
void dfs(int x,int fa) {
deep[x]=deep[fa]+1;
anc[x][0]=fa;
for(re i=1; i<=17; i++) {
anc[x][i]=anc[anc[x][i-1]][i-1];
}
minv[x]=x;
for(int i=head[x]; i; i=w[i].next) {
int nxt=w[i].v;
dfs(nxt,x);
if(minv[nxt]<minv[x]) {
minv[x]=minv[nxt];
}
}
}
int tim,dfn[Size];
struct node {
int x,t;
};
inline bool operator < (const node a,const node b) {
return a.t<b.t;
}
inline bool operator > (const node a,const node b) {
return a.t>b.t;
}
void dfs2(int x) {
vector<node> son;
for(re i=head[x]; i; i=w[i].next) {
int nxt=w[i].v;
if(nxt!=anc[x][0]) {
son.push_back((node){nxt,minv[nxt]});
}
}
sort(son.begin(),son.end());
int len=son.size();
for(int i=0; i<len; i++) {
int nxt=son[i].x;
dfs2(nxt);
}
dfn[x]=++tim;
}
priority_queue<node,vector<node>,greater<node> > Heap; //小根堆
//小根堆裏記錄所有沒有球的節點,以dfn爲關鍵字
bool ball[Size];
inline int insert() {
node tp=Heap.top();
Heap.pop();
ball[tp.x]=true;
return tp.x; //返回落到的位置
}
inline int pop(int num) {
int x=num;
for(re i=17; i>=0; i--) {
if(ball[anc[x][i]]) {
x=anc[x][i];
}
}
ball[x]=false;
Heap.push((node){x,dfn[x]});
return deep[num]-deep[x];
}
int main() {
// freopen("15.in","r",stdin);
n=read();
q=read();
int root=0;
for(re i=1; i<=n; i++) {
int fa=read();
if(!fa) {
root=i;
} else {
AddEdge(fa,i);
}
}
dfs(root,0);
dfs2(root);
for(re i=1; i<=n; i++) {
// printf("%d ",dfn[i]);
Heap.push((node){i,dfn[i]});
}
// putchar(10);
while(q--) {
int op=read();
int num=read();
if(op==1) {
for(re i=1; i<num; i++) {
insert();
}
printf("%d\n",insert());
} else {
printf("%d\n",pop(num));
}
}
return 0;
}
/*
15 8
5 9 11 3 10 3 13 13 11 12 12 0 10 5 9
1 8
2 13
2 7
2 7
2 8
2 2
2 1
1 4
*/