1. 程式人生 > 實用技巧 >《洛谷P3373 【模板】線段樹 2》

《洛谷P3373 【模板】線段樹 2》

這題很早之前就做過,當時沒理解透,現在理清楚了,感覺很多好東西。

首先,我們需要明確,區間操作加和區間乘,顯然要lazytag。

因為是加和乘兩種操作,我們去維護兩個tag : adtag - 需要下放的加的值 , mutag - 需要下放的乘的值。

對於區間加這種操作,我們直接去像普通的線段樹一樣去加上代價即可。

對於區間乘,首先,我們的mutag *= k,然後sum *= k,此時需要注意,我們adtag的值可能還累計的很多沒有下放,如果我這裡不對adtag操作。

後面下放後,就少去了 * k的代價,所以我們需要讓adtag *= k,這樣才是正確的需要下放的和代價。

然後主要就是pushdown操作:

首先,

對於左右孩子sum值的更新,首先很顯然要加上父親的adtag(注意要乘上區間長度,因為我們維護的是單個下放代價)。

同時由於我們一直在動態地維護adtag的值,所以我們的mutag 沒有必要 * adtag了,就乘上自己即可。

這裡也就是題解區一直說的加法優先的概念。

對於左右孩子的mutag ,顯然都乘上父親的mutag 即可。

對於左右孩子的adtag,同理要考慮到累計的adtag的代價,所以要先 *= 父親的mutag 再去加父親的adtag。

#include<bits/stdc++.h>
using namespace std;
typedef long long
LL; typedef pair<LL,int> pii; const int N = 1e5+5; const int M = 1e6+5; const LL Mod = 998244353; #define pi acos(-1) #define INF 1e18 #define CT0 cin.tie(0),cout.tie(0) #define IO ios::sync_with_stdio(false) #define dbg(ax) cout << "now this num is " << ax << endl; namespace FASTIO{ inline LL read(){ LL x
= 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } } using namespace FASTIO; LL MD; LL MUL(LL a,LL b){return a * b % MD;} LL ADD(LL a,LL b){return (a + b) % MD;} struct Node{int L,r;LL adtag,mutag,sum;}node[N << 2]; int a[N]; void Pushup(int idx) { node[idx].sum = ADD(node[idx << 1].sum,node[idx << 1 | 1].sum); } void Pushdown(int idx) { int adtag = node[idx].adtag,mutag = node[idx].mutag; int lslen = node[idx << 1].r - node[idx << 1].L + 1; int rslen = node[idx << 1 | 1].r - node[idx << 1 | 1].L + 1; node[idx << 1].sum = ADD(MUL(node[idx << 1].sum,mutag),MUL(lslen,adtag)); node[idx << 1 | 1].sum = ADD(MUL(node[idx << 1 | 1].sum,mutag),MUL(rslen,adtag)); node[idx << 1].adtag = ADD(MUL(node[idx << 1].adtag,mutag),adtag); node[idx << 1].mutag = MUL(node[idx << 1].mutag,mutag); node[idx << 1 | 1].adtag = ADD(MUL(node[idx << 1 | 1].adtag,mutag),adtag); node[idx << 1 | 1].mutag = MUL(node[idx << 1 | 1].mutag,mutag); node[idx].adtag = 0,node[idx].mutag = 1; } void build(int L,int r,int idx) { node[idx].L = L,node[idx].r = r; node[idx].adtag = node[idx].sum = 0; node[idx].mutag = 1; if(L == r) { node[idx].sum = a[L] % MD; return ; } int mid = (L + r) >> 1; build(L,mid,idx << 1); build(mid + 1,r,idx << 1 | 1); Pushup(idx); } void tree_mul(int L,int r,int idx,int k) { if(node[idx].L >= L && node[idx].r <= r) { node[idx].sum = MUL(node[idx].sum,k); node[idx].adtag = MUL(node[idx].adtag,k); node[idx].mutag = MUL(node[idx].mutag,k); return ; } int mid = (node[idx].L + node[idx].r) >> 1; Pushdown(idx); if(mid >= L) tree_mul(L,r,idx << 1,k); if(mid < r) tree_mul(L,r,idx << 1 | 1,k); Pushup(idx); } void tree_add(int L,int r,int idx,int k) { if(node[idx].L >= L && node[idx].r <= r) { node[idx].sum = ADD(node[idx].sum,MUL(node[idx].r - node[idx].L + 1,k)); node[idx].adtag = ADD(node[idx].adtag,k); return ; } int mid = (node[idx].L + node[idx].r) >> 1; Pushdown(idx); if(mid >= L) tree_add(L,r,idx << 1,k); if(mid < r) tree_add(L,r,idx << 1 | 1,k); Pushup(idx); } int query(int L,int r,int idx) { if(node[idx].L >= L && node[idx].r <= r) return node[idx].sum; int mid = (node[idx].L + node[idx].r) >> 1,ans = 0; Pushdown(idx); if(mid >= L) ans = ADD(ans,query(L,r,idx << 1)); if(mid < r) ans = ADD(ans,query(L,r,idx << 1 | 1)); return ans; } int main() { int n,m;n = read(),m = read(),MD = read(); for(int i = 1;i <= n;++i) a[i] = read(); build(1,n,1); while(m--) { int op,x,y;op = read(),x = read(),y = read(); if(op == 1) { int k;k = read(); tree_mul(x,y,1,k); } else if(op == 2) { int k;k = read(); tree_add(x,y,1,k); } else printf("%d\n",query(x,y,1)); } return 0; }
View Code