[BZOJ4003]城池攻佔
Description
小銘銘最近獲得了一副新的桌遊,遊戲中需要用 m 個騎士攻佔 n 個城池。
這 n 個城池用 1 到 n 的整數表示。除 1 號城池外,城池 i 會受到另一座城池 fi 的管轄,
其中 fi <i。也就是說,所有城池構成了一棵有根樹。這 m 個騎士用 1 到 m 的整數表示,其
中第 i 個騎士的初始戰鬥力為 si,第一個攻擊的城池為 ci。
每個城池有一個防禦值 hi,如果一個騎士的戰鬥力大於等於城池的生命值,那麼騎士就可
以佔領這座城池;否則佔領失敗,騎士將在這座城池犧牲。佔領一個城池以後,騎士的戰鬥力
將發生變化,然後繼續攻擊管轄這座城池的城池,直到佔領 1 號城池,或犧牲為止。
除 1 號城池外,每個城池 i 會給出一個戰鬥力變化引數 ai;vi。若 ai =0,攻佔城池 i 以後騎士戰鬥力會增加 vi;若 ai =1,攻佔城池 i 以後,戰鬥力會乘以 vi。注意每個騎士是單獨計算的。也就是說一個騎士攻擊一座城池,不管結果如何,均不會影響其他騎士攻擊這座城
的結果。
現在的問題是,對於每個城池,輸出有多少個騎士在這裡犧牲;對於每個騎士,輸出他攻佔的城池數量。Input
第 1 行包含兩個正整數 n;m,表示城池的數量和騎士的數量。
第 2 行包含 n 個整數,其中第 i 個數為 hi,表示城池 i 的防禦值。
第 3 到 n +1 行,每行包含三個整數。其中第 i +1 行的三個數為 fi;ai;vi,分別表示管轄
這座城池的城池編號和兩個戰鬥力變化引數。
第 n +2 到 n + m +1 行,每行包含兩個整數。其中第 n + i 行的兩個數為 si;ci,分別表
示初始戰鬥力和第一個攻擊的城池。Output
輸出 n + m 行,每行包含一個非負整數。其中前 n 行分別表示在城池 1 到 n 犧牲的騎士
數量,後 m 行分別表示騎士 1 到 m 攻佔的城池數量。Sample Input
5 550 20 10 10 30
1 1 2
2 0 5
2 0 -10
1 0 10
20 2
10 3
40 4
20 4
35 5
Sample Output
2
0
0
0
1
1
3
1
1
HINT
對於 100% 的資料,1 <= n;m <= 300000; 1 <= fi<i; 1 <= ci <= n; -10^18 <= hi,vi,si <= 10^18;ai等於1或者2;當 ai =1 時,vi > 0;保證任何時候騎士戰鬥力值的絕對值不超過 10^18。
首先我們在每個節點死的是一段子樹內的點經各種變化後得到的最小值,為了維護這個過程,我們考慮堆
然後我們發現乘法沒有負數,因此兩個點同時向上走以後之間的大小關係就不會改變了
因此我們直接在左偏樹上打標記即可
程式碼:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define ls ch[x][0] 6 #define rs ch[x][1] 7 #define int long long 8 #define M 300010 9 using namespace std; 10 int read() 11 { 12 char ch=getchar();int x=0,f=1; 13 while(ch>'9'||ch<'0') {if(ch=='-') f=-1;ch=getchar();} 14 while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); 15 return x*f; 16 } 17 int n,m,num; 18 int head[M],rt[M],a[M],v[M],dis[M],val[M],tag1[M],tag2[M],ch[M][2]; 19 int fa[M],deep[M],kill[M],dead[M],c[M],h[M]; 20 struct point{int to,next;}e[M<<1]; 21 void add(int from,int to) 22 { 23 e[++num].next=head[from]; 24 e[num].to=to; 25 head[from]=num; 26 } 27 void pushdown(int x,int t1,int t2) 28 { 29 val[x]*=t1,val[x]+=t2; 30 tag1[x]*=t1;tag2[x]*=t1,tag2[x]+=t2; 31 } 32 void push(int x) 33 { 34 pushdown(ls,tag1[x],tag2[x]); 35 pushdown(rs,tag1[x],tag2[x]); 36 tag1[x]=1,tag2[x]=0; 37 } 38 int merge(int x,int y) 39 { 40 if(!x||!y) return x+y; 41 push(x),push(y); 42 if(val[x]>val[y]) swap(x,y); 43 ch[x][1]=merge(rs,y); 44 if(dis[ls]<dis[rs]) swap(ls,rs); 45 dis[x]=dis[rs]+1; 46 return x; 47 } 48 void dfs(int x) 49 { 50 deep[x]=deep[fa[x]]+1; 51 for(int i=head[x];i;i=e[i].next) 52 { 53 int to=e[i].to;dfs(to); 54 rt[x]=merge(rt[x],rt[to]); 55 } 56 while(rt[x]&&val[rt[x]]<h[x]) 57 { 58 push(rt[x]); 59 dead[x]++; 60 kill[rt[x]]=deep[c[rt[x]]]-deep[x]; 61 rt[x]=merge(ch[rt[x]][0],ch[rt[x]][1]); 62 } 63 if(a[x]) pushdown(rt[x],v[x],0); 64 else pushdown(rt[x],1,v[x]); 65 } 66 #undef int 67 int main() 68 { 69 #define int long long 70 n=read();m=read(); 71 for(int i=1;i<=n;i++) h[i]=read(); 72 for(int i=2;i<=n;i++) 73 { 74 fa[i]=read(),a[i]=read(); 75 v[i]=read(),add(fa[i],i); 76 } 77 for(int i=1;i<=m;i++) 78 { 79 val[i]=read(),c[i]=read(); 80 tag1[i]=1,tag2[i]=0; 81 rt[c[i]]=merge(rt[c[i]],i); 82 } 83 dfs(1); 84 while(rt[1])//如果一直沒死 85 { 86 push(rt[1]); 87 kill[rt[1]]=deep[c[rt[1]]]; 88 rt[1]=merge(ch[rt[1]][0],ch[rt[1]][1]); 89 } 90 for(int i=1;i<=n;i++) printf("%lld\n",dead[i]); 91 for(int i=1;i<=m;i++) printf("%lld\n",kill[i]); 92 return 0; 93 }