[关闭]
@Aonrbet 2020-07-06T02:39:39.000000Z 字数 3903 阅读 393

透彻线段树

Study


1.

区间加

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. /*
  4. 如果人可以长尾巴
  5. 会觉得有点难为情呢
  6. 因为只要和你在一起,我总会忍不住摇尾巴吧
  7. */
  8. const int maxn = 1e6 + 10;
  9. #define ls(now) (now << 1)
  10. #define rs(now) (now<<1|1)
  11. #define mid ((l + r) >> 1)
  12. int n, m, a[maxn];
  13. struct seg_tree{
  14. struct nodes{
  15. long long l, r, sum, tag;
  16. long long get(){
  17. return sum + (r - l + 1) * tag;
  18. }
  19. }node[maxn];
  20. void up(int now){
  21. return (void)(node[now].sum = node[ls(now)].get() + node[rs(now)].get());
  22. }
  23. void down(int now){
  24. return (void)(node[ls(now)].tag += node[now].tag, node[rs(now)].tag += node[now].tag, node[now].tag = 0);
  25. }
  26. void bulid(int l, int r, int now){
  27. node[now].l = l, node[now].r = r;
  28. if(l == r) return (void)(node[now].sum = a[l]);
  29. bulid(l, mid, ls(now)), bulid(mid+1, r, rs(now));
  30. up(now);
  31. }
  32. void chenge(int l, int r, int now, int val){
  33. if(node[now].r < l or node[now].l > r) return;
  34. if(l <= node[now].l and node[now].r <= r) return (void)(node[now].tag += val);
  35. down(now);
  36. chenge(l, r, ls(now), val), chenge(l, r, rs(now), val);
  37. up(now);
  38. }
  39. void query(int l, int r, int now, long long &ans){
  40. if(l > node[now].r or r < node[now].l) return;
  41. if(l <= node[now].l and node[now].r <= r) return (void)(ans += node[now].get());
  42. down(now);
  43. query(l, r, ls(now), ans), query(l, r, rs(now), ans);
  44. up(now);
  45. }
  46. }tree;
  47. signed main(){
  48. scanf("%d%d", &n, &m);
  49. for(int i = 1; i <= n; i ++){
  50. scanf("%d", &a[i]);
  51. }
  52. tree.bulid(1, n, 1);
  53. for(int cmp, x, y, z; m ; m --){
  54. scanf("%d", &cmp);
  55. if(cmp == 1){
  56. scanf("%d%d%d", &x, &y, &z);
  57. tree.chenge(x, y, 1, z);
  58. }
  59. if(cmp == 2){
  60. long long ans = 0;
  61. scanf("%d%d", &x, &y);
  62. tree.query(x, y, 1, ans);
  63. printf("%lld\n", ans);
  64. }
  65. }
  66. return 0;
  67. }

2.

区间乘+区间加

加法和乘法顺序不一样会导致不同的结果

比如: 不等于

而在记录懒标记的时候,加法和乘法两种标记放到一起,并不知道哪个先,哪个后。

所以要确定一个优先级

我们分析一下两种顺序:

  1. 先加后乘 : =

  2. 先乘后加:

比较一下,发现,上面的先加后乘相当于下面的式子,在加法上面多乘了一个

所以,我们只要是先加后乘的式子,只要加一个就可以转化为先乘后加的式子

具体的操作就是在添加乘法标记的时候,把加法标记就好了

所以,我们就定了一个总顺序:先乘后加(摘自luogu博客)

然后在down下放标记的时候,左右儿子的加法标记传递也要保持先乘后加的顺序,即 :

  1. void down(int now){
  2. t[ls(now)].tmp = (t[ls(now)].tmp*t[now].tmp)%p;
  3. t[rs(now)].tmp = (t[rs(now)].tmp*t[now].tmp)%p;
  4. t[ls(now)].tag = (t[ls(now)].tag*t[now].tmp)%p;
  5. t[rs(now)].tag = (t[rs(now)].tag*t[now].tmp)%p;
  6. t[ls(now)].tag = (t[ls(now)].tag+t[now].tag)%p;
  7. t[rs(now)].tag = (t[rs(now)].tag+t[now].tag)%p;
  8. t[now].tag = 0, t[now].tmp = 1;
  9. return;
  10. }

解决方法:先乘后加

  1. //对于每一次的区间乘val,我们对在此次操作前做的区间加也乘上这次的val
  2. t[now].tmp *= val, t[now].tag *= val

完美的解决了问题

  1. #include <bits/stdc++.h>
  2. using namespace std;
  3. /*
  4. 如果人可以长尾巴
  5. 会觉得有点难为情呢
  6. 因为只要和你在一起,我总会忍不住摇尾巴吧
  7. */
  8. const int maxn = 1e6 + 10;
  9. #define ls(now) (now << 1)
  10. #define rs(noe) (now<<1|1)
  11. #define mid ((l + r) >> 1)
  12. int n, m, p, a[maxn];
  13. long long ans;
  14. struct seg_tree{
  15. struct nodes{
  16. long long l, r, sum, tag, tmp;
  17. nodes(){
  18. tmp = 1;
  19. tag = 0;
  20. }
  21. long long get(){
  22. return (((sum%p)*tmp%p)%p + ((r-l+1)*tag)%p);
  23. }
  24. }t[maxn];
  25. void up(int now){
  26. return (void)(t[now].sum = t[ls(now)].get() + t[rs(now)].get());
  27. }
  28. void down(int now){
  29. t[ls(now)].tmp = (t[ls(now)].tmp*t[now].tmp)%p;
  30. t[rs(now)].tmp = (t[rs(now)].tmp*t[now].tmp)%p;
  31. t[ls(now)].tag = (t[ls(now)].tag*t[now].tmp)%p;
  32. t[rs(now)].tag = (t[rs(now)].tag*t[now].tmp)%p;
  33. t[ls(now)].tag = (t[ls(now)].tag+t[now].tag)%p;
  34. t[rs(now)].tag = (t[rs(now)].tag+t[now].tag)%p;
  35. t[now].tag = 0, t[now].tmp = 1;
  36. return;
  37. }
  38. void bulid(int l, int r, int now){
  39. t[now].l = l, t[now].r = r;
  40. if(l == r) return (void)(t[now].sum = a[l]);
  41. bulid(l, mid, ls(now)), bulid(mid+1, r, rs(now));
  42. up(now);
  43. }
  44. void chenge(int l, int r, int now, int val){
  45. if(r < t[now].l or l > t[now].r) return;
  46. if(l <= t[now].l and t[now].r <= r) return (void)(t[now].tag += val);
  47. down(now);
  48. chenge(l, r, ls(now), val), chenge(l, r, rs(now), val);
  49. up(now);
  50. }
  51. void change(int l, int r, int now, int val){
  52. if(r < t[now].l or l > t[now].r) return;
  53. if(l <= t[now].l and t[now].r <= r) return (void)(t[now].tmp *= val, t[now].tag *= val);
  54. down(now);
  55. change(l, r, ls(now), val), change(l, r, rs(now), val);
  56. up(now);
  57. }
  58. void query(int l, int r, int now, long long &ans){
  59. if(r < t[now].l or l > t[now].r) return;
  60. if(l <= t[now].l and t[now].r <= r) return (void)(ans += t[now].get(), ans %= p);
  61. down(now);
  62. query(l, r, ls(now), ans), query(l, r, rs(now), ans);
  63. up(now);
  64. }
  65. }tree;
  66. signed main(){
  67. scanf("%d%d%d", &n, &m, &p);
  68. for(int i = 1; i <= n; i ++){
  69. scanf("%d", &a[i]);
  70. }
  71. tree.bulid(1, n, 1);
  72. for(int cmp, x, y, z; m; m --){
  73. scanf("%d", &cmp);
  74. if(cmp == 1){
  75. scanf("%d%d%d", &x, &y, &z);
  76. tree.change(x, y, 1, z);
  77. }
  78. if(cmp == 2){
  79. scanf("%d%d%d", &x, &y, &z);
  80. tree.chenge(x, y, 1, z);
  81. }
  82. if(cmp == 3){
  83. scanf("%d%d", &x, &y);
  84. tree.query(x, y, 1, ans);
  85. printf("%lld\n", ans);
  86. ans = 0;
  87. }
  88. }
  89. return 0;
  90. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注