P3373 【模板】线段树 2

题目描述

如题,已知一个数列,你需要进行下面三种操作:

  • 将某区间每一个数乘上 x;
  • 将某区间每一个数加上 x;
  • 求出某区间每一个数的和。

输入格式

第一行包含三个整数 n,q,m,分别表示该数列数字的个数、操作的总个数和模数。

第二行包含 n 个用空格分隔的整数,其中第 i 个数字表示数列第 i 项的初始值。

接下来 q 行每行包含若干个整数,表示一个操作,具体如下:

操作 1: 格式:1 x y k 含义:将区间 [x,y] 内每个数乘上 k

操作 2: 格式:2 x y k 含义:将区间 [x,y] 内每个数加上 k

操作 3: 格式:3 x y 含义:输出区间 [x,y] 内每个数的和对 m 取模所得的结果

输出格式

输出包含若干行整数,即为所有操作 33 的结果。

输入输出样例

输入 #1

5 5 38
1 5 4 2 3
2 1 4 1
3 2 5
1 2 4 2
2 3 5 5
3 1 4

输出 #1

17
2

说明/提示

【数据范围】

对于 30%30% 的数据:n≤8,q≤10。
对于 70%70% 的数据:n≤10^3,q≤10^4。
对于 100%100% 的数据:1≤n≤10^5,1≤q≤10^5。

除样例外,m=571373。

(数据已经过加强 ^_^)

样例说明:

故输出应为 1717、22(40 mod 38=240mod38=2)。

思路:

本题关键是如何处理乘和加的关系,很自然的想到是确定乘和加的先后顺序,但是可以通过乘法分配率将加法与乘法合并。(代码太长了,很容易错,我错了好多次/(ㄒoㄒ)/~~)

代码:

#include<bits/stdc++.h>
using namespace std;
#define int long long
struct node{
	int l,r;
	int da;
	int lz;
	int lz1;
};
node t[400005];
int a[100005];
int n,m,mod;

void push_down(int i)
{
	if(t[i].lz!=0){
	t[i*2].lz=(t[i*2].lz+t[i].lz)%mod;
    t[i*2+1].lz=(t[i*2+1].lz+t[i].lz)%mod;
    int mid=(t[i].l+t[i].r)/2;
    t[i*2].da=(t[i*2].da+t[i].lz*(mid-t[i*2].l+1))%mod;
    t[i*2+1].da=(t[i*2+1].da+t[i].lz*(t[i*2+1].r-mid))%mod;
    t[i].lz=0;
	}
    return ;
}

void push_down1(int i)
{
	if(t[i].lz1!=1){
	t[i*2].lz1=(t[i*2].lz1*t[i].lz1)%mod;
    t[i*2+1].lz1=(t[i*2+1].lz1*t[i].lz1)%mod;
    
    t[i*2].lz=(t[i*2].lz*t[i].lz1)%mod;
    t[i*2+1].lz=(t[i*2+1].lz*t[i].lz1)%mod;
    
    t[i*2].da=(t[i].lz1*t[i*2].da)%mod;
    t[i*2+1].da=(t[i].lz1*t[i*2+1].da)%mod;
    t[i].lz1=1;	
	}
    return ;
}

void build(int l,int r,int pot){
	t[pot].l=l;
	t[pot].r=r;
	t[pot].lz=0;
	t[pot].lz1=1;
	if(l==r){
		t[pot].da=a[l]%mod;
		return ;
	}
	int mid=(l+r)/2;
	build(l,mid,pot*2);
	build(mid+1,r,pot*2+1);
	t[pot].da=(t[pot*2].da+t[pot*2+1].da)%mod;
	return ;
}
void updata(int l,int r,int k,int pot){
	if(l<=t[pot].l&&t[pot].r<=r){
		t[pot].da=(t[pot].da+k*(t[pot].r-t[pot].l+1)%mod)%mod;
		t[pot].lz=(t[pot].lz+k)%mod;
		return ;
	}
	push_down1(pot);
	push_down(pot);
	if(t[pot*2].r>=l){
		updata(l,r,k,pot*2);
	}
	if(t[pot*2+1].l<=r){
		updata(l,r,k,pot*2+1);
	}
	t[pot].da=(t[pot*2].da+t[pot*2+1].da)%mod;
}

void updata1(int l,int r,int k,int pot){
	if(l<=t[pot].l&&t[pot].r<=r){
		t[pot].da=(k*t[pot].da)%mod;
		t[pot].lz1=(t[pot].lz1*k)%mod;
		t[pot].lz=(t[pot].lz*k)%mod;
		return ;
	}
	push_down1(pot);
	push_down(pot);
	if(t[pot*2].r>=l){
		updata1(l,r,k,pot*2);
	}
	if(t[pot*2+1].l<=r){
		updata1(l,r,k,pot*2+1);
	}
	t[pot].da=(t[pot*2].da+t[pot*2+1].da)%mod;
}

int search(int i,int l,int r){
    if(t[i].l>=l && t[i].r<=r)
        return t[i].da%mod;
    if(t[i].r<l || t[i].l>r)  return 0;
    push_down1(i);
   	push_down(i);
    int s=0;
    if(t[i*2].r>=l)  s=(s+search(i*2,l,r))%mod;
    if(t[i*2+1].l<=r)  s=(s+search(i*2+1,l,r))%mod;
    return s;
}

signed main(){
	scanf("%lld%lld%lld", &n, &m, &mod);
	for(int i=1;i<=n;i++)
	scanf("%lld", &a[i]);
	build(1,n,1);
	while(m--){
		int op;
		int x,y;
		long long k;
		scanf("%lld", &op);
		if(op==1){
			scanf("%lld%lld%lld", &x, &y, &k);
			updata1(x,y,k,1);
		}
		else if(op==2){
			scanf("%lld%lld%lld", &x, &y, &k);
			updata(x,y,k,1);
		}else {
			scanf("%lld%lld", &x, &y);
			printf("%lld\n",search(1,x,y)%mod);
		}
	}
	return 0;
}

 

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值