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只考虑当前区间影响区间内的和。
建树时对于叶子节点就很容易了,直接赋值就行了。下面考虑怎么合并,分三种情况:
- add(ls(x))==del(rs(x)),相当于x节点的右儿子正好删掉了左儿子的所有元素,于是 del(x)=del(ls(x)),add(x)=add(rs(x)),val(x)=val(rs(x);
- add(ls(x))< del(rs(x)),x节点的右儿子删掉了左儿子的所有元素还要多,于是del(x)=dal(ls(x))+del(rs(x))-add(ls(x)),add(x)=add(rs(x)),val(x)=val(rs(x));
- add(ls(x))> del(rs(x)),x节点的有儿子没有全部删掉左儿子的所有元素,于是 del(x)=del(ls(x));add(x)=add(rs(x))+add(ls(x))-del(rs(x));val(x)=val(rs(x))+cal(ls(x),del(rs(x)));
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 }