HZOJ Weed
作者的題解:
如果一段操作被執行,會對整個棧有什么影響呢?
把棧彈出若干個數后再插入若干個數。
線段樹:
每個點紀錄三個值:執行完這段操作后會刪多少個,再插多少個,插的和一共是多少。
合并值時再用一個函數查找左孩子被從右刪除若干個后剩下的插入總和是多少。
建樹復雜度O( N log N ), 單次查詢復雜度O( log ^ 2 N), 總復雜度O( N log N + Q log ^2 N ).
樹袋熊學長的題解:
? 實際上,每個加數和刪除的操作可以看作是入棧和彈棧操作,之后可以用線段樹維護多個操作間的關系。線段樹的下標是操作時間,由于我們想得到整個序列經過修改操作后的結果,因此線段
樹上維護四個信息:
s:區間內加數總和(僅考慮區間內部影響);
nd:當前區間向前刪除數字的數量;
na:當前區間內“凈”加的元素數。
sd:當前區間被右兄弟刪除后的總和。 ※僅對左兒子維護此信息。
? 我們通過維護以上四個標記,就可以得到答案(root->s)。現在我們考慮如何維護信息。
首先,在建樹或修改時,只需要將葉子節點的前三個標記維護好即可;在遞歸返回時,再計算最后一個。如果左兒子不夠右兒子刪,那么非常簡單,直接利用這幾個標記計算即可,l->sd=0。
比較麻煩的是左兒子不被刪光的情況。我們先實現一個函數cal(x),利用sd標記計算在當前區間中刪去x個元素后的和
? 有了這個函數,我們就可以非常方便地計算左兒子的sd,維護其
他標記了,不再贅述。
由于這個奇怪的函數在每層節點都會調用,而一共有O(logn)層節點,所以線段樹操作的時間復雜度變為O(log 2 n),總時間復雜度也比常規的線段樹多一個log。
? 難點主要在于使用額外的函數維護信息,并正確的討論各種情況。
? 考試時一定要想清楚,時間復雜度是靠“鏈狀”延伸的維護函數保障的,如果不小心寫成了兩邊都下去,就變成O(n 2 )了。
?還是說我自己咋寫的吧,
用線段樹維護操作(好象是第二次碰到這種題),對于線段樹的每個節點,維護三個信息,del當前節點刪除其左兄弟幾個元素,add只考慮當前區間影響區間內的元素個數,val只考慮當前區間影響區間內的和。
建樹時對于葉子節點就很容易了,直接賦值就行了。下面考慮怎么合并,分三種情況:
cal(k,x)表示k節點的區間刪掉結尾x個元素后的和:
int cal(int k,int x) {if(x==add(rs(k)))return val(k)-val(rs(k));if(x<add(rs(k))) return val(k)-val(rs(k))+cal(rs(k),x);if(x>add(rs(k))) return cal(ls(k),x-add(rs(k))+del(rs(k))); }?到這里大概就沒什么了。
最后答案為val(1),對于每次修改直接遞歸到對應葉子節點即可。
?
1 #include<iostream> 2 #include<cstdio> 3 #define MAXN 200100 4 #define LL long long 5 using namespace std; 6 struct tree 7 { 8 int l,r,del,add,val; 9 #define l(x) tr[x].l 10 #define r(x) tr[x].r 11 #define del(x) tr[x].del 12 #define add(x) tr[x].add 13 #define val(x) tr[x].val 14 #define ls(x) (x<<1) 15 #define rs(x) (ls(x)+1) 16 }tr[MAXN*10]; 17 int m,q,k[MAXN],v[MAXN]; 18 int cal(int k,int x) 19 { 20 if(x==add(rs(k)))return val(k)-val(rs(k)); 21 if(x<add(rs(k))) return val(k)-val(rs(k))+cal(rs(k),x); 22 if(x>add(rs(k))) return cal(ls(k),x-add(rs(k))+del(rs(k))); 23 } 24 void updata(int x) 25 { 26 if(add(ls(x))==del(rs(x))) 27 { 28 del(x)=del(ls(x));add(x)=add(rs(x));val(x)=val(rs(x)); 29 } 30 if(add(ls(x))<del(rs(x))) 31 { 32 del(x)=del(ls(x))+del(rs(x))-add(ls(x)); 33 add(x)=add(rs(x)); 34 val(x)=val(rs(x)); 35 } 36 if(add(ls(x))>del(rs(x))) 37 { 38 del(x)=del(ls(x)); 39 add(x)=add(rs(x))+add(ls(x))-del(rs(x)); 40 val(x)=val(rs(x))+cal(ls(x),del(rs(x))); 41 } 42 } 43 void build(int x,int l,int r) 44 { 45 l(x)=l,r(x)=r; 46 if(l==r) 47 { 48 if(k[l]==0) del(x)=0,val(x)=v[l],add(x)=1; 49 else del(x)=v[l],val(x)=0,add(x)=0; 50 return; 51 } 52 int mid=(l+r)>>1; 53 build(ls(x),l,mid); 54 build(rs(x),mid+1,r); 55 updata(x); 56 } 57 void change(int x,int t,int k,int v) 58 { 59 if(l(x)==r(x)) 60 { 61 if(k==0)del(x)=0,val(x)=v,add(x)=1; 62 else del(x)=v,val(x)=0,add(x)=0; 63 return; 64 } 65 int mid=(l(x)+r(x))>>1; 66 if(t<=mid)change(ls(x),t,k,v); 67 else change(rs(x),t,k,v); 68 updata(x); 69 } 70 signed main() 71 { 72 /// freopen("weed.in","r",stdin); 73 74 cin>>m>>q; 75 for(int i=1;i<=m;i++) 76 cin>>k[i]>>v[i]; 77 build(1,1,m); 78 int c,kk,vv; 79 for(int i=1;i<=q;i++) 80 { 81 cin>>c>>kk>>vv; 82 change(1,c,kk,vv); 83 cout<<val(1)<<endl; 84 } 85 } View Code?
轉載于:https://www.cnblogs.com/Al-Ca/p/11331095.html
總結
- 上一篇: 改造我们的学习:有钱不会花,抱着金库抓瞎
- 下一篇: HZOJ Drink