P3373 模板线段树 2 {线段树,模板}
Posted c-come
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3373 模板线段树 2 {线段树,模板}相关的知识,希望对你有一定的参考价值。
OMG_Data_Structure So_Interesting_Mother-Fucker(译:数据结构,奥妙重重)
虽然只是模板,但还是挺麻烦的,可见数据结构都是毒瘤。
已知一个数列,你需要进行下面三种操作:
操作1: 格式:1 x y k 含义:将区间[x,y]内每个数乘上k
操作2: 格式:2 x y k 含义:将区间[x,y]内每个数加上k
操作3: 格式:3 x y 含义:输出区间[x,y]内每个数的和对P取模所得的结果
首先一定要用lazy标记,不然妥妥TLE。
这道题比最基本的模板多了乘法和取模,但加法和乘法这两个玩意和取模是很友好的,所以很简单。
通过思考加法和乘法的运算律,可以写出如下代码。
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #define repo(i,a,b) for(int i = a;i <= b;++i) 5 #define repi(i,a,b) for(int i = a;i >= b;++i) 6 using namespace std; 7 8 struct segment_tree{ 9 int l,r; 10 long long sum,add,mul;//sum为区间和,add为加法懒惰标记,mul为乘法懒惰标记 11 #define l(x) tree[x].l 12 #define r(x) tree[x].r 13 #define sum(x) tree[x].sum 14 #define add(x) tree[x].add 15 #define mul(x) tree[x].mul//节省寿命防止眼瞎的宏定义 16 } tree[100002*4]; 17 long long a[100002],n,m,mod,oper,x,y,k;//oper表示操作编号 18 19 inline int read(){//快读 20 long long s=0,w=1; 21 char ch = getchar(); 22 while(ch < ‘0‘ || ch > ‘9‘){ 23 if(ch == ‘-‘)w = -1; 24 ch = getchar(); 25 } 26 while(ch >= ‘0‘ && ch <= ‘9‘){ 27 s = s*10+ch-‘0‘; 28 ch = getchar(); 29 } 30 return w*s; 31 } 32 33 inline void build(int p,int l,int r){//建树 34 l(p) = l,r(p) = r; 35 mul(p) = 1;//别忘了初始化mul(p),C++11才支持struct内non_static变量的初始化 36 if(l==r){sum(p) = a[l] % mod;return;} 37 int mid = (l+r)/2; 38 build(p*2,l,mid); 39 build(p*2+1,mid+1,r); 40 sum(p) = (sum(p*2) + sum(p*2+1)) % mod; 41 } 42 43 inline int len(int p){//节省寿命防止眼瞎的函数 44 return r(p)-l(p)+1; 45 } 46 47 inline void spread(int p){ 48 if(add(p) || mul(p)!=1){//如果有标记要传的话 49 sum(p*2) = sum(p*2)*mul(p) % mod; 50 sum(p*2+1) = sum(p*2+1)*mul(p) % mod; 51 mul(p*2) = mul(p*2)*mul(p) % mod; 52 mul(p*2+1) = mul(p*2+1)*mul(p) % mod; 53 add(p*2) = add(p*2)*mul(p) % mod; 54 add(p*2+1) = add(p*2+1)*mul(p) % mod;//子树的add也会受到乘法的影响,即(a+add)*mul 55 mul(p) = 1;//mul(p) down 56 57 sum(p*2) = (sum(p*2)+add(p)*len(p*2)) % mod; 58 sum(p*2+1) = (sum(p*2+1)+add(p)*len(p*2+1)) % mod; 59 add(p*2) = (add(p*2)+add(p)) % mod; 60 add(p*2+1) = (add(p*2+1)+add(p)) % mod; 61 add(p) = 0;//add(p) down 62 } 63 }//除了把p*2和p*2+1各自都写出来,还可以写一个devide_spread()函数 64 //调用devide_spread(p*2),devide_spread(p*2+1) 65 66 inline void oper_add(int p,int l,int r,int d){//加法操作 67 if(l <= l(p) && r >= r(p)){ 68 sum(p) = (sum(p)+(long long)d*len(p)) % mod; 69 add(p) = (add(p)+d) % mod; 70 return; 71 } 72 spread(p); 73 int mid = (l(p)+r(p))/2; 74 if(l <= mid)oper_add(p*2,l,r,d); 75 if(r > mid)oper_add(p*2+1,l,r,d); 76 sum(p) = (sum(p*2)+sum(p*2+1)) % mod; 77 } 78 79 inline void oper_mul(int p,int l,int r,int d){//乘法操作 80 if(l <= l(p) && r >= r(p)){ 81 sum(p) = (sum(p)*d) % mod; 82 add(p) = (add(p)*d) % mod;//同spread()函数里的解释 83 mul(p) = (mul(p)*d) % mod; 84 return; 85 } 86 spread(p); 87 int mid = (l(p)+r(p))/2; 88 if(l <= mid)oper_mul(p*2,l,r,d); 89 if(r > mid)oper_mul(p*2+1,l,r,d); 90 sum(p) = (sum(p*2)+sum(p*2+1)) % mod; 91 } 92 93 inline long long ask(int p,int l,int r){//询问 94 if(l <= l(p) && r >= r(p))return sum(p) % mod; 95 spread(p); 96 int mid = (l(p)+r(p))/2; 97 long long val = 0; 98 if(l <= mid)val = (val+ask(p*2,l,r)) % mod; 99 if(r > mid)val = (val+ask(p*2+1,l,r)) % mod; 100 return val; 101 } 102 103 int main(){ 104 n = read(),m = read(),mod = read(); 105 repo(i,1,n){ 106 a[i] = read(); 107 } 108 build(1,1,n); 109 repo(i,1,m){ 110 oper = read(); 111 if(oper == 1){ 112 x = read(),y = read(),k = read(); 113 oper_mul(1,x,y,k); 114 } 115 if(oper == 2){ 116 x = read(),y = read(),k = read(); 117 oper_add(1,x,y,k); 118 } 119 if(oper == 3){ 120 x = read(),y = read(); 121 printf("%lld ",ask(1,x,y)); 122 } 123 } 124 return 0; 125 }
以上是关于P3373 模板线段树 2 {线段树,模板}的主要内容,如果未能解决你的问题,请参考以下文章