Weed
Time Limit: 20 Sec Memory Limit: 512 MB
Description
从前有个栈,一开始是空的。
你写下了 m 个操作,每个操作形如 k v :
若 k = 0,代表往栈顶加入一个数 v
若 k = 1,则代表从栈顶弹出 v 个数,如果栈中的元素少于 v 个,则全部弹出。
接着你又进行了 q 次修改,每次你会选择一个操作,并且修改它的两个参数。
在每次修改后,你都要求出如果依次执行这些操作,最后栈中剩下的元素之和。
第一行两个正整数 m,q,分别表示操作数和修改次数。
接下来 m 行,每行两个整数 k,v,代表一个操作。
接下来 q 行,每行三个正整数 c,k,v,表示将第 c 个操作的参数修改为 k 和 v。
Output
输出 q 行,每行一个整数,代表依次执行所有操作后栈中剩下的元素之和。
5 2
0 3
0 2
0 3
1 1
0 5
1 0 3
1 0 1
Sample Output
10
8
HINT
m,q ≤ 2×1e5, v ≤ 1e4
Solution
首先,我们可以把一个操作拆成:先删除若干个数,然后加入若干个数 。
那么我们可以用线段树来维护,一个节点记录:删除del个数 ,加入add个数 ,这add个数的和是val 。
那么我们只需要支持单点修改 ,答案显然就是Node[1].val 。问题在于怎么合并两个节点 的信息。
我们分情况讨论,记录左儿子为L ,右儿子为R 。显然信息形如:----+++ / -----+++ 。讨论一下 R.del 与 L.add 的关系:
1. 显然当 L.add <= R.del 的时候, del 即为 L.del + R剩余的del ,add 即为 R.add ,val 即为 R.val ;
2. 否则,当 L.add > R.del 的时候,难点在于 L 剩下多少 val ,只要讨论出了这个问题,就解决了该题。
我们令函数 Query(i, k) 表示 删除 了节点 i 的 后 k 个值 ,剩下的 val 。那么显然这个也只要分类讨论即可:
1. k = R.add ,返回 i.val - R.val 即可,比较显然;
2. k < R.add ,显然我们需要继续往 R 递归 ,返回 i.val - R.val + Query(R, k) ;
3. k > R.add ,显然我们需要往 L 递归 ,显然 k 先减去 R.add ,又因为存在R.del这一段 ,所以 L 的后面几个 是被删除 的,要多查几个 ,所以返回 Query(L, k - R.add + R.del) 。
然后我们写个线段树 ,就解决了这道题啦QWQ。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 #include <bits/stdc++.h> using namespace std;typedef long long s64;const int ONE = 1e6 + 5 ;int m, T;struct point { int opt, val; }oper[ONE]; struct power { int add, del, val; }Node[ONE * 4 ]; int get () { int res=1 ,Q=1 ;char c; while ( (c=getchar ())<48 || c>57 ) if (c=='-' )Q=-1 ; res=c-48 ; while ( (c=getchar ())>=48 && c<=57 ) res=res*10 +c-48 ; return res*Q; } int Query (int i, int k) { int L = i << 1 , R = i << 1 | 1 ; if (k == Node[R].add) return Node[i].val - Node[R].val; if (k < Node[R].add) return Node[i].val - Node[R].val + Query (R, k); if (k > Node[R].add) return Query (L, k - Node[R].add + Node[R].del); } power Merge (int L, int R) { power c = (power){0 , 0 , 0 }; if (Node[L].add <= Node[R].del) c.del = Node[L].del + Node[R].del - Node[L].add, c.add = Node[R].add, c.val = Node[R].val; if (Node[L].add > Node[R].del) { c.del = Node[L].del; c.add = Node[L].add - Node[R].del + Node[R].add; c.val = Query (L, Node[R].del) + Node[R].val; } return c; } void Build (int i, int l, int r) { if (l == r) { if (oper[l].opt == 0 ) Node[i] = (power){1 , 0 , oper[l].val}; else Node[i] = (power){0 , oper[l].val, 0 }; return ; } int mid = l + r >> 1 ; Build (i << 1 , l, mid); Build (i << 1 | 1 , mid + 1 , r); Node[i] = Merge (i << 1 , i << 1 | 1 ); } void Update (int i, int l, int r, int L) { if (L <= l && r <= L) { if (oper[l].opt == 0 ) Node[i] = (power){1 , 0 , oper[l].val}; else Node[i] = (power){0 , oper[l].val, 0 }; return ; } int mid = l + r >> 1 ; if (L <= mid) Update (i << 1 , l, mid, L); else Update (i << 1 | 1 , mid + 1 , r, L); Node[i] = Merge (i << 1 , i << 1 | 1 ); } int main () { m = get (); T = get (); for (int i = 1 ; i <= m; i++) oper[i].opt = get (), oper[i].val = get (); Build (1 , 1 , m); while (T--) { int id = get (); oper[id].opt = get (); oper[id].val = get (); Update (1 , 1 , m, id); printf ("%d\n" , Node[1 ].val); } }