1. 程式人生 > >【P3373】 【模板】線段樹 2 {線段樹,模板}

【P3373】 【模板】線段樹 2 {線段樹,模板}

inter ati 裏的 編號 運算 static變量 long ios inline

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\n",ask(1,x,y)); 122 } 123 } 124 return 0; 125 }

【P3373】 【模板】線段樹 2 {線段樹,模板}