线段树模板题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==sumk+addk
这样来实现 add 和 sum 有序进行。

pushdown操作:
现在要下传两个标记:addmul

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操作的主要内容,如果未能解决你的问题,请参考以下文章

[知识点]线段树标记永久化

线段树专题

线段树标记永久化

BZOJ2157: 旅游 树链剖分 线段树

线段树模板加模板题POJ3468

[POI2006]TET-Tetris 3D