bzoj1798 [Ahoi2009]Seq 维护序列seq
1798: [Ahoi2009]Seq 维护序列seq
Time Limit: 30 Sec Memory Limit: 64 MBSubmit: 5375 Solved: 1899
[Submit][Status][Discuss]
Description
Input
Output
Sample Input
1 2 3 4 5 6 7
5
1 2 5 5
3 2 4
2 3 7 9
3 1 3
3 4 7
Sample Output
35
8
HINT
【样例说明】
初始时数列为(1,2,3,4,5,6,7)。
经过第1次操作后,数列为(1,10,15,20,25,6,7)。
对第2次操作,和为10+15+20=45,模43的结果是2。
经过第3次操作后,数列为(1,10,24,29,34,15,16}
对第4次操作,和为1+10+24=35,模43的结果是35。
对第5次操作,和为29+34+15+16=94,模43的结果是8。
测试数据规模如下表所示
数据编号 1 2 3 4 5 6 7 8 9 10
N= 10 1000 1000 10000 60000 70000 80000 90000 100000 100000
M= 10 1000 1000 10000 60000 70000 80000 90000 100000 100000
Source
分析:很显然,本题要用线段树做,但是怎么用线段树倒是个问题.可以想到先建树,每次修改一个区间的值把这个区间所包含的区间的值给修改了即可,但是这样太花时间了,有没有省时间的方法呢?一个著名的思想lazy-tag就出现了,想要详细了解lazy-tag,可以取看看这篇博客:传送门.
简单来说,如果要修改一个区间的值,如果这个区间刚好被完全包含了就直接返回,打上标记(要改成多少),那么下次要用子节点的时候将标记下传即可,显然,对于本题要打两个tag,那么sum[o] = sum[o] * mul[o] + add[o],sum为和,mul为乘的数,add为加的数,那么如果我们乘一个数c,sum[o] = sum[o] * mul[o] * c + add[o] * c,将mul[o] * c和add[o] * c看作两个整体,那么可以发现如果乘一个数c的话,add数组要*c,mul数组也要*c,同理,如果加一个数c,那么只需要add数组+c即可,因为在下传标记的时候既有加,又有减(可能为0),所以add和mul数组的计算一定要将这两种情况都计算到.
如果在线段树上几个区间操作的性质相同的,先把要求的数用操作需要的量给表示出来,然后对于每一种操作看看需要维护的标记的变化,最后合并一下即可.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define le l,mid,o * 2 #define re mid + 1,r,o * 2 + 1 using namespace std; const long long maxn = 1000010; long long n, p,a[maxn],add[maxn],mul[maxn],sum[maxn],m; void build(int l, int r, int o) { mul[o] = 1; add[o] = 0; if (l == r) { sum[o] = a[l]; return; } int mid = (l + r) >> 1; build(le); build(re); sum[o] = (sum[o * 2] + sum[o * 2 + 1]) % p; } void pushdown(int o, int k) { sum[o * 2] = (sum[o * 2] * mul[o] + add[o] * (k - (k >> 1))) % p; sum[o * 2 + 1] = (sum[o * 2 + 1] * mul[o] + add[o] * (k >> 1)) % p; mul[o * 2] = mul[o * 2] * mul[o] % p; mul[o * 2 + 1] = mul[o * 2 + 1] * mul[o] % p; add[o * 2] = (add[o * 2] * mul[o] + add[o]) % p; add[o * 2 + 1] = (add[o * 2 + 1] * mul[o] + add[o]) % p; mul[o] = 1; add[o] = 0; } void jia(int l, int r, int o, int x, int y, int v) { if (x <= l && r <= y) { add[o] = (add[o] + v) % p; sum[o] = (sum[o] + v * (r - l + 1)) % p; return; } pushdown(o, r - l + 1); int mid = (l + r) >> 1; if (x <= mid) jia(le, x, y, v); if (y > mid) jia(re, x, y, v); sum[o] = (sum[o * 2] + sum[o * 2 + 1]) % p; } void cheng(int l, int r, int o, int x, int y, int v) { if (x <= l && r <= y) { add[o] = (add[o] * v) % p; mul[o] = (mul[o] * v) % p; sum[o] = (sum[o] * v) % p; return; } pushdown(o, r - l + 1); int mid = (l + r) >> 1; if (x <= mid) cheng(le, x, y, v); if (y > mid) cheng(re, x, y, v); sum[o] = (sum[o * 2] + sum[o * 2 + 1]) % p; } long long query(int l, int r, int o, int x, int y) { if (x <= l && r <= y) return sum[o] % p; int mid = (l + r) >> 1; pushdown(o, r - l + 1); long long temp = 0; if (x <= mid) temp = (temp + query(le, x, y)) % p; if (y > mid) temp = (temp + query(re, x, y)) % p; sum[o] = (sum[o * 2] + sum[o * 2 + 1]) % p; return temp % p; } int main() { scanf("%lld%lld", &n, &p); for (int i = 1; i <= n; i++) scanf("%lld", &a[i]); build(1, n, 1); scanf("%lld", &m); while (m--) { int op, t, g,c; scanf("%d%d%d", &op, &t, &g); if (op == 1) { scanf("%d", &c); cheng(1, n, 1, t, g, c); } if (op == 2) { scanf("%d", &c); jia(1, n, 1, t, g, c); } if (op == 3) printf("%lld\n", query(1, n, 1, t, g) % p); } return 0; }