洛谷 P3373 【模板】线段树 2

重温一下线段树,听说蓝桥杯线段树考的比较多,过来复习一下,一个线段树的板子题。
题目大意:
支持区间加、区间乘、区间查询
要求时间复杂度为nlog(n)级别
显然线段树(树状数组忘记怎么写了QAQ~ )
如果只有区间加法或者只有区间乘法,本题将直接做标记,下放标记即可,单次修改或查询最坏情况下时间复杂度为树的高度,即:log(n),总体的时间复杂度为mlog(n)
而本题有加法和乘法两种标记,所以本题的核心就是怎么处理加法和乘法标记的关系
在这里插入图片描述
如上图,可以解读为:
(x+3)* 5 ---------------------------------------(1)
(x*5)+ 3 ---------------------------------------(2)
为了避免产生歧义我们必须规定一个先后顺序。
假设我们先算的是加法
①若: + 3 比 *5 先运行,则直接计算(1)式
②若: *5 比 + 3 先运行
此时, jia 无可避免的会乘以 cheng 很难计算出正确的数值
假设我们先算的是乘法
①若: + 3 比 *5 先运行,则我们在 * 5 的时候,对于还没有下方的 + 3 也要进行 * 5 的计算即将(1)式拆开
x * 5 + 3 * 5 ---------------------------------------(3)
②若: *5 比 + 3 先运行,则直接计算(2)式
故,我们先处理乘法标记

xf_cheng(root);  //先下放
xf_jia(root);    //后下放

下放乘法标记

void xf_cheng(zt *root)
{
	root->lift->cheng *= root->cheng;  //向左
	root->lift->cheng %= p;
	root->right->cheng *= root->cheng;  //向右
	root->right->cheng %= p;
	root->lift->jia *= root->cheng;  //处理之前的加法,类似上文(1)式
	root->lift->jia %= p;
	root->right->jia *= root->cheng;
	root->right->jia %= p;
	root->lift->sum *= root->cheng;  //处理上文x,此处用sum表示的
	root->lift->sum %= p;
 	root->right->sum *= root->cheng;
 	root->right->sum %= p;
	root->cheng = 1;  //下放后清除标记为1
}

下放加法标记

void xf_jia(zt *root) 
{
	root->lift->jia += root->jia;
	root->right->jia += root->jia;
	root->lift->sum += root->jia * root->lift->sz;
	root->lift->sum %= p;
 	root->right->sum += root->jia * root->right->sz;
 	root->right->sum %= p;
	root->jia = 0;
}

完整代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 4e5 + 5;
struct zt
{
	ll l,r,sum,sz,jia,cheng;
	zt *lift,*right;
};
zt tree[N];
int n,m,p,cnt,a,x,y,k;
void xf_cheng(zt *root)
{
	root->lift->cheng *= root->cheng;
	root->lift->cheng %= p;
	root->right->cheng *= root->cheng;
	root->right->cheng %= p;
	root->lift->jia *= root->cheng;
	root->lift->jia %= p;
	root->right->jia *= root->cheng;
	root->right->jia %= p;
	root->lift->sum *= root->cheng;
	root->lift->sum %= p;
 	root->right->sum *= root->cheng;
 	root->right->sum %= p;
	root->cheng = 1;
}
void xf_jia(zt *root) 
{
	root->lift->jia += root->jia;
	root->right->jia += root->jia;
	root->lift->sum += root->jia * root->lift->sz;
	root->lift->sum %= p;
 	root->right->sum += root->jia * root->right->sz;
 	root->right->sum %= p;
	root->jia = 0;
}
void build_tree(zt *root,ll l,ll r)
{
	root->sum = 0;root->l = l;root->r = r;
	root->sz = r - l + 1;root->jia = 0;root->cheng = 1;
	if(l == r) return;
	root->lift = &tree[++cnt];
	root->right = &tree[++cnt];
	ll mid = l + r >> 1;
	build_tree(root->lift,l,mid);
	build_tree(root->right,mid + 1,r);
}
void add(zt *root,ll l,ll r,ll ql,ll qr,ll x)
{
	if(l >= ql&&qr >= r)
	{
		root->sum += root->sz * x;
		root->sum %= p;
		root->jia += x;
		root->jia %= p;
		return;
	}
	xf_cheng(root);
	xf_jia(root);
	ll mid = l + r >> 1;
	if(ql <= mid) add(root->lift,l,mid,ql,qr,x);
	if(qr > mid) add(root->right,mid + 1,r,ql,qr,x);
	root->sum = root->lift->sum + root->right->sum;
	root->sum %= p;
}
void cheng(zt *root,ll l,ll r,ll ql,ll qr,ll x)
{
	if(l >= ql&&qr >= r)
	{
		root->sum *= x;
		root->sum %= p;
		root->cheng *= x;
		root->cheng %= p;
		root->jia *= x;
		root->jia %= p;
		return;
	}
	xf_cheng(root);
	xf_jia(root);
	ll mid = l + r >> 1;
	if(ql <= mid) cheng(root->lift,l,mid,ql,qr,x);
	if(qr > mid) cheng(root->right,mid + 1,r,ql,qr,x);
	root->sum = root->lift->sum + root->right->sum;	
	root->sum %= p;
}
ll sum(zt *root,ll l,ll r,ll ql,ll qr)
{
	if(l >= ql&&qr >= r)
		return root->sum % p;
	xf_cheng(root);
	xf_jia(root);
	ll mid = l + r >> 1,ans = 0;
	if(ql <= mid) ans = sum(root->lift,l,mid,ql,qr);
	if(qr > mid) ans += sum(root->right,mid + 1,r,ql,qr);
	root->sum = root->lift->sum + root->right->sum;	
	root->sum %= p;
	return ans % p; 
}
int main()
{
	cin>>n>>m>>p;
	build_tree(tree,1,n); 
	for(int i = 1;i <= n;i ++) 
	{
		cin>>a;
		add(tree,1,n,i,i,a);
	} 
	for(int i = 1;i <= m;i ++)
	{
		cin>>a;
		if(a == 3)
		{
			cin>>x>>y;
			cout<<sum(tree,1,n,x,y) % p<<'\n';
		}
		else
		{
			cin>>x>>y>>k;
			if(a == 2)
				add(tree,1,n,x,y,k);
			else
				cheng(tree,1,n,x,y,k);
		}
	}
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值