线段树模板题pushup+pushdown操作
Posted 行码棋
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树模板题pushup+pushdown操作相关的知识,希望对你有一定的参考价值。
- 博客主页: https://blog.csdn.net/qq_50285142
- 欢迎点赞👍收藏✨关注❤留言 📝 如有错误,敬请指正
- 🎈虽然生活很难,但我们也要一直走下去🎈
题目链接
虽然是模板题,但是两种操作确实还是挺麻烦人的。
操作都是对区间进行修改,所以需要用到懒标记,设两个懒标记,分别针对加和乘。
区间乘法:
先把 mul 和 sum 乘上 k 。
对于之前已经有的 add ,把它乘上 k 即可。在这里,我们把乘之后的值直接更新add的值。
add 其实应该加到 sum 里面,所有乘上 k 后,运用乘法分配律, ( s u m + a d d ) ∗ k = = s u m ∗ k + a d d ∗ k (sum + add) * k == sum * k + add * k (sum+add)∗k==sum∗k+add∗k 。
这样来实现 add 和 sum 有序进行。
pushdown操作:
现在要下传两个标记:add
和 mul
sum :因为 add 之前已经乘过,所以在子孩子乘过 mul 后直接加就行。
mul :直接乘上去
add :因为 add 的值是要包括乘之后的值,所以子孩子要先乘上 mul
#include<bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef vector<int> vi;
typedef vector<ll> vl;
const int dx[]={-1,0,1,0},dy[]={0,1,0,-1};
const int inf = 0x3f3f3f3f;
const ll linf = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-6;
const int mod = 1e9+7;
const int N = 1e5+5,M = 2e5+5;
int n,m,p;
int a[N];
struct node
{
int l,r;
ll sum,add,mul;
}tr[4*N];
void pushup(int u)
{
tr[u].sum = (tr[u<<1].sum + tr[u<<1|1].sum) % p;
}
void pushdown(int u)
{
auto &root = tr[u],&left = tr[u<<1],&right = tr[u<<1|1];
left.add = (left.add*root.mul+root.add)%p;
left.mul = (left.mul*root.mul)%p;
left.sum = (left.sum*root.mul+(left.r-left.l+1)*root.add)%p;
right.add = (right.add*root.mul+root.add)%p;
right.mul = (right.mul*root.mul)%p;
right.sum = (right.sum*root.mul+(right.r-right.l+1)*root.add)%p;
root.add = 0;
root.mul = 1;
}
void build(int u,int l,int r)
{
tr[u] = {l,r};
tr[u].mul = 1;
if(l==r)
{
tr[u].sum = a[l]%p;
return;
}
int mid = l + r >> 1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
int query(int u,int l,int r)
{
if(tr[u].l >= l and tr[u].r <= r) return tr[u].sum;
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
ll s = 0;
if(l<=mid) s = (s+query(u<<1,l,r))%p;
if(r>mid) s = (s+query(u<<1|1,l,r))%p;
return s;
}
void modify1(int u,int l,int r,int v)
{
if(tr[u].l >= l and tr[u].r <= r)
{
tr[u].sum = (tr[u].sum+(tr[u].r-tr[u].l+1)*v)%p;
tr[u].add = (tr[u].add + v)%p;
}
else
{
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) modify1(u<<1,l,r,v);
if(r > mid) modify1(u<<1|1,l,r,v);
pushup(u);
}
}
void modify2(int u,int l,int r,int v)
{
if(tr[u].l >= l and tr[u].r <= r)
{
tr[u].add = (tr[u].add*v)%p;
tr[u].sum = (tr[u].sum*v)%p;
tr[u].mul = (tr[u].mul*v)%p;
}
else
{
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid) modify2(u<<1,l,r,v);
if(r > mid) modify2(u<<1|1,l,r,v);
pushup(u);
}
}
void solve()
{
cin>>n>>m>>p;
for(int i=1;i<=n;i++) cin>>a[i];
build(1,1,n);
for(int i=1;i<=m;i++)
{
int x,y,k,f;
cin>>f>>x>>y;
if(f==3) cout<<query(1,x,y)<<'\\n';
else
{
cin>>k;
if(f==1) modify2(1,x,y,k);
else modify1(1,x,y,k);
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int _;
// cin>>_;
_ = 1;
while(_--)
{
solve();
}
return 0;
}
往期优质文章推荐
以上是关于线段树模板题pushup+pushdown操作的主要内容,如果未能解决你的问题,请参考以下文章