P3373线段树模板

题目链接:【模板】线段树 2 - 洛谷

这题总共有三种操作:

  • 将某区间每一个数乘上 x

  • 将某区间每一个数加上 x

  • 求出某区间每一个数的和

add和multi这两种修改,在节点上分别使用两个Lazy-Tag标记,这两个标记的关系如下:

  1. 做add修改时,跟普通标记一样,没什么特别需要注意的地方
  2. 做multi修改时,如果原先有add标记,将add标记改写为 add*multi,这个时候的子树的值就可以由 子树的值=子树的值*父节点的multi的标记数+父节点的add标记数*区间大小 得到

其中具体函数的操作代码如下:

  1.  做线段树的pushdown操作时,需要注意,先进行子区间的修改,然后进行add和multi操作的下传,具体操作看下面代码:
    void pushdown(int p,int lp,int rp)
    {
    	int mid=(lp+rp)>>1;
    	sum[ls(p)]=sum[ls(p)]*multi[p]+add[p]*(mid-lp+1);//左子树上的值变为原来的值*父节点的multi标记+父节点的add标记*区间大小
    	sum[rs(p)]=sum[rs(p)]*multi[p]+add[p]*(rp-mid);//右子树同上
    	multi[ls(p)]=multi[ls(p)]*multi[p];
    	multi[rs(p)]=multi[rs(p)]*multi[p];
    	add[ls(p)]=add[ls(p)]*multi[p]+add[p];//左子树上的add标记变为(add*父节点的multi标记+父节点的add标记)
    	add[rs(p)]=add[rs(p)]*multi[p]+add[p];//右子树同上
    	multi[p]=1;//乘的标记初值为1
    	add[p]=0;
    }
  2. ls函数和rs函数如下(求左右节点位置的函数):

    int ls(int p) { return p<<1; }
    int rs(int p) { return p<<1|1; }
  3. 求区间和的操作代码:

    int query(int x,int y,int p,int lp,int rp)
    {
    	if(x<=lp&&y>=rp)//当该区间处于所求区间中间时
    		return sum[p];
    	pushdown(p,lp,rp);
    	int mid=(lp+rp)>>1;
    	long long ans=0;
    	if(x<=mid) ans+=query(x,y,ls(p),lp,mid);
    	if(y>mid)  ans+=query(x,y,rs(p),mid+1,rp);
    	return ans;
    }
  4. 将区间上每个点都加上x:
    void update1(int x,int y,int k,int p,int lp,int rp)
    {
    	if(x<=lp&&y>=rp)
    	{
    		add[p]+=k;
    		sum[p]=sum[p]+k*(rp-lp+1);
    		return;
    	}
    	pushdown(p,lp,rp);
    	int mid=(lp+rp)>>1;
    	if(x<=mid) update1(x,y,k,ls(p),lp,mid);
    	if(y>mid) update1(x,y,k,rs(p),mid+1,rp);
    	pushup(p);
    }
  5. 将区间上每个点都乘上x:
    void update2(int x,int y,int k,int p,int lp,int rp)
    {
    	if(x<=lp&&y>=rp)
    	{
    		sum[p]=sum[p]*k;
    		multi[p]=multi[p]*k;
    		add[p]=add[p]*k;//这里就需要变为add*k,便于子节点的计算与传递
    		return;
    	}
    	pushdown(p,lp,rp);
    	int mid=(lp+rp)>>1;
    	if(x<=mid) update2(x,y,k,ls(p),lp,mid);
    	if(y>mid)  update2(x,y,k,rs(p),mid+1,rp);
    	pushup(p);
    }

上述代码只是为了做一个简单讲述,因为题目要求有取模运算,以及可想而知的数肯定很大,所以需要开long long类型。

具体ac代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<algorithm>
#define ll long long
using namespace std;
long long add[100005<<2]={0},sum[100005<<2],multi[100005<<2]={0};
long long arr[100005];
 ll n,m,mod;
ll ls(ll p) { return p<<1; }
ll rs(ll p) { return p<<1|1; }
void pushup(ll p){
	sum[p]=sum[ls(p)]+sum[rs(p)];
	sum[p]%=mod;
}
void build(ll p,ll lp,ll rp)
{
	multi[p]=1;
	if(lp==rp)
	{
		sum[p]=arr[lp];
	    return ;
    }
	ll mid=(lp+rp)>>1;
	build(ls(p),lp,mid);
	build(rs(p),mid+1,rp);
	pushup(p);
}
void pushdown(ll p,ll lp,ll rp)
{
	ll mid=(lp+rp)>>1;
	sum[ls(p)]=(sum[ls(p)]*multi[p]+add[p]*(mid-lp+1))%mod;
	sum[rs(p)]=(sum[rs(p)]*multi[p]+add[p]*(rp-mid))%mod;
	multi[ls(p)]=(multi[ls(p)]*multi[p])%mod;
	multi[rs(p)]=(multi[rs(p)]*multi[p])%mod;
	add[ls(p)]=(add[ls(p)]*multi[p]+add[p])%mod;
	add[rs(p)]=(add[rs(p)]*multi[p]+add[p])%mod;
	multi[p]=1;
	add[p]=0;
}
void update1(ll x,ll y,ll k,ll p,ll lp,ll rp)
{
	if(x<=lp&&y>=rp)
	{
		add[p]+=k;
		sum[p]=(sum[p]+k*(rp-lp+1))%mod;
		return;
	}
	pushdown(p,lp,rp);
	ll mid=(lp+rp)>>1;
	if(x<=mid) update1(x,y,k,ls(p),lp,mid);
	if(y>mid) update1(x,y,k,rs(p),mid+1,rp);
	pushup(p);
}
void update2(ll x,ll y,ll k,ll p,ll lp,ll rp)
{
	if(x<=lp&&y>=rp)
	{
		sum[p]=(sum[p]*k)%mod;
		multi[p]=multi[p]*k%mod;
		add[p]=(add[p]*k)%mod;
		return;
	}
	pushdown(p,lp,rp);
	ll mid=(lp+rp)>>1;
	if(x<=mid) update2(x,y,k,ls(p),lp,mid);
	if(y>mid)  update2(x,y,k,rs(p),mid+1,rp);
	pushup(p);
}
long long query(ll x,ll y,ll p,ll lp,ll rp)
{
	if(x<=lp&&y>=rp)
	return sum[p];
	pushdown(p,lp,rp);
	ll mid=(lp+rp)>>1;
	long long ans=0;
	if(x<=mid) ans+=query(x,y,ls(p),lp,mid);
	if(y>mid)  ans+=query(x,y,rs(p),mid+1,rp);
	return ans;
}
int main(){
	scanf("%lld %lld %lld",&n,&m,&mod);
	for(int i=1;i<=n;i++)
		scanf("%lld",&arr[i]);
	build(1,1,n);
		while(m--)
		{
		ll num,x,y,k;
		scanf("%lld%lld%lld",&num,&x,&y);
		if(num==1){
			scanf("%lld",&k);
			update2(x,y,k,1,1,n);
		}
		else if(num==2){
			scanf("%lld",&k);
		    update1(x,y,k,1,1,n);
		}
		else
			printf("%lld\n",query(x,y,1,1,n)%mod);
	}
	return 0;
}

光看果然是不行的,实践是检验真理的唯一标准果然没错,学了好久的线段树了,结果这题我还是写了半天,到处找资料,属实是没脸见人了,果然还是得多刷题啊,希望以后越来越好qwq

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值