Ants on tree
Time Limit: 1000 ms Memory Limit: 256 MB
Description
從前有一個策略遊戲, 叫做 螞蟻上樹
遊戲中有一棵 \(n\) 個節點, 以 1 為根的有根樹
初始始每個節點都為空, 遊戲系統會進行兩種操作 :
1 x , 表示往 \(x\) 節點放入一只睡眠狀態中的螞蟻
2 x , 表示從 \(x\) 節點取出一只睡眠狀態中的螞蟻
(對於操作2, 保證取出前該點至少有一只螞蟻)
每次操作後, 玩家要進行一輪遊戲 :
遊戲有無窮的時間, 每一時刻, 系統會 依次執行 下述五個操作
1) 讓玩家選擇 任意多只(可以為 0 只) 睡眠狀態中的螞蟻
3) 若某一時刻 ≥2≥2只 亢奮狀態 的螞蟻處在同一節點, 遊戲失敗
4) 到達根節點的螞蟻進入睡眠狀態.
5) 當前時刻被玩家選擇的螞蟻進入亢奮狀態
6) 若所有螞蟻都在根節點, 遊戲結束
遊戲不允許失敗, 玩家的遊戲目的是 : 使遊戲結束時, 最後一只到達根節點的螞蟻到達時間最早.
每輪遊戲後, 系統會自動將樹恢復成玩家該輪遊戲前的局面, 然後進行下一次取/放螞蟻的操作.
Input
第一行兩個數 \(n,m\) 表示樹的點數和操作數
第 \(2?n\) 行, 第 \(i\) 行一個數 \(f_i\) 表示 \(i\)
接下來\(m\) 行, 每行兩個數表示系統的操作
若為 1 x , 表示往 \(x\) 節點放入一只睡眠狀態中的螞蟻
若為 2 x , 表示從 \(x\) 節點取出一只睡眠狀態中的螞蟻
Output
輸出 mm 行, 表示每輪遊戲在最優策略下
最後一只到達根節點的螞蟻到達的最早時間
(特別的, 如果所有螞蟻都在根節點, 或者沒有螞蟻, 輸出 0)
Sample Input
4 5
1
2
2
1 1
1 3
1 4
1 2
2 3
Sample Output
0
3
4
4
3
HINT
對於樣例輸出第四行的解釋 :
第一時刻觸碰位於 2, 3 的那只螞蟻, 他們進入亢奮狀態但沒有移動
第三時刻不觸碰螞蟻, 當前位於 2, 4 的螞蟻分別爬到 1, 2, 爬到 1 的這只螞蟻進入睡眠狀態
第四時刻不觸碰螞蟻, 當前位於 2 的螞蟻爬到 1 並進入睡眠狀態, 然後遊戲結束
數據範圍 :
對於 30%的數據,$ n,m≤3000$
對於另外 30% 的數據, \(n≤5000\)
對於另外 5%的數據, 樹的最大深度為 \(2\)
對於另外 10%的數據, 數據的生成方式如下 \(f_i=\)rand()%(i?1)+1
對於 100%的數據 :\(2≤n≤10^5\),\(1≤m≤10^5\),\(1≤f_i<i,i=2..n\),\(1≤x≤n\)
Solution
- 30%
好像有一種線段樹暴力維護的方法?mnlogn的應該是
- 100%
依舊是一種用線段樹爆搞的方法
(然而其實還有一種很優秀的做法是用平衡樹來維護然而我太菜了不會qwq)
首先分析一下問題,要求不能有兩只亢奮狀態的螞蟻撞在一起,而螞蟻的“行進速度”又是一樣的,稍微思考一下會發現,其實只要兩只螞蟻到達終點(也就是根節點)的時間不同,這兩只螞蟻肯定不會撞上
所以現在的問題就變成了,要給每只螞蟻分配一個到達終點的時間,並且這個時間要大於或者等於這只螞蟻所在節點的深度
?
然後就考慮怎麽維護了,我們考慮以到達根節點的時間為區間種一棵線段樹,最底層的節點維護能在大於等於這個時間點到達根節點的螞蟻的數量,這裏有一種十分神秘的維護方式(然而還是被用平衡樹的dalao們D飛了qwq):
對於一只螞蟻,我們只要給它分配一個大於等於其節點深度的到達時間就好了,也就是說對於一只在節點\(x\)的螞蟻來說,到達時間在\([dep[x],maxdep]\)這段區間都是ok的,所以這只螞蟻的到達時間應該是這段區間內第一個為\(0\)的時間點(為啥是第一個?因為要求總時間最少嘛)
然而實際上,我們並不用明確每只螞蟻具體在什麽時間到達,我們只要知道每個時間區間可以有多少只螞蟻選擇就好了,因為其實誰先誰後並不重要,照這樣的思路,插入操作就變成了給線段樹的\(dep[x]\)到\([dep[x],maxdep]\)中第一個為\(0\)的時間點這段區間加上\(1\),說明這只螞蟻可以選在這段區間中的某個時間點到達根節點
這樣的思考方式就為刪除提供了便利
對於刪除,因為每只螞蟻這樣看來都是一樣的,所以為了讓答案更優,我們只要找到\([dep[x],maxdep]\)中最後的那個為\(1\)的時間點(記為\(loc\) )然後把這個時間點的螞蟻刪掉就好了,具體實現起來就是給\([dep[x],loc]\)這個區間減去1,說明能在這段區間達到根節點的螞蟻少了一只
對於答案的統計,就是找到最後一個可能有螞蟻選擇的位置\(x\),然後最終的\(ans\)就是\(x\)加上可能選擇大於等於這個時間點的螞蟻的數量
具體實現也是比較簡單的,對於每個區間維護一個最大值和一個最小值爆搞即可
代碼大概長這個樣子
#include<iostream>
#include<cstdio>
#include<cstring>
#define lch ch[x][0]
#define rch ch[x][1]
using namespace std;
const int MAXN=1e5+10,SEG=MAXN*8;
int rt[MAXN];
namespace Seg{/*{{{*/
int ch[SEG][2],sz[SEG],tag[SEG],mn[SEG],mx[SEG];
int tot,n;
void _build(int x,int l,int r){
sz[x]=0;
if (l==r) return;
int mid=l+r>>1;
lch=++tot; _build(lch,l,mid);
rch=++tot; _build(rch,mid+1,r);
}
void pushup(int x){
mn[x]=min(mn[lch],mn[rch]);
mx[x]=max(mx[lch],mx[rch]);
}
void givetag(int x,int delta){mn[x]+=delta; mx[x]+=delta; tag[x]+=delta;}
void downtag(int x){
if (!tag[x]) return;
if (lch) givetag(lch,tag[x]);
if (rch) givetag(rch,tag[x]);
tag[x]=0;
}
void build(int _n){tot=1;n=_n;_build(1,1,n);}
int newnode(){
ch[++tot][0]=ch[tot][1]=sz[tot]=0;
return tot;
}
void _update(int x,int l,int r,int lx,int rx,int delta){
if (l<=lx&&rx<=r){
givetag(x,delta);
return;
}
downtag(x);
int mid=lx+rx>>1;
if (l<=mid) _update(lch,l,r,lx,mid,delta);
if (r>mid) _update(rch,l,r,mid+1,rx,delta);
pushup(x);
}
void update(int l,int r,int delta){_update(1,l,r,1,n,delta);}
int _find(int x,int d,int lx,int rx,int delta){
if (mn[x]>delta) return rx+1;
if (lx==rx) return lx;
downtag(x);
int mid=lx+rx>>1,ret=0;
if (d<=mid) ret=_find(lch,d,lx,mid,delta);
if (d>mid||ret==mid+1) ret=_find(rch,d,mid+1,rx,delta);
return ret;
}
int find(int d,int delta){return _find(1,d,1,n,delta);}
int _query(int x,int lx,int rx){
if (!mx[x]) return 0;
if (lx==rx) return lx+mx[x];
downtag(x);
int mid=lx+rx>>1;
if (mx[rch]) return _query(rch,mid+1,rx);
return _query(lch,lx,mid);
}
int query(){return _query(1,1,n);}
};/*}}}*/
struct xxx{
int y,nxt;
}a[MAXN*2];
int h[MAXN],dep[MAXN],f[MAXN],cnt[MAXN],tm[MAXN];
int n,m,tot,all,ans,mxdep;
void add(int x,int y);
void dfs(int fa,int x,int d);
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
#endif
int x,y,op;
scanf("%d%d",&n,&m);
bool flag=true;
memset(h,-1,sizeof(h));
tot=0;
for (int i=2;i<=n;++i){
scanf("%d",f+i);
add(f[i],i);
}
Seg::build(n*4);
ans=0;mxdep=0;
dfs(0,1,0);
int tmp;
for (int i=1;i<=m;++i){
scanf("%d%d",&op,&x);
if (op==1){
if (x!=1){
tmp=Seg::find(dep[x],0);
Seg::update(dep[x],min(tmp,mxdep),1);
}
}
else{
if (x!=1){
tmp=Seg::find(dep[x],1);
Seg::update(dep[x],min(tmp,mxdep),-1);
}
}
ans=Seg::query();
printf("%d\n",ans);
}
}
void add(int x,int y){
a[++tot].y=y; a[tot].nxt=h[x]; h[x]=tot;
}
void dfs(int fa,int x,int d){
int u; dep[x]=d; mxdep=max(dep[x],mxdep);
for (int i=h[x];i!=-1;i=a[i].nxt){
u=a[i].y;
if (u==fa) continue;
dfs(x,u,d+1);
}
}
Ants on tree