线 段 树 2 线段树 2 线段树2
题目链接:luogu 3373
题目
已知一个数列,你需要进行下面三种操作:
- 将某区间每一个数乘上 x x x
- 将某区间每一个数加上 x x x
- 求出某区间每一个数的和
输入
第一行包含三个整数
N
N
N、
M
M
M、
P
P
P,分别表示该数列数字的个数、操作的总个数和模数。
第二行包含
N
N
N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。
接下来
M
M
M行每行包含
3
3
3或
4
4
4个整数,表示一个操作,具体如下:
操作1: 格式:
1
1
1
x
x
x
y
y
y
k
k
k 含义:将区间
[
x
,
y
]
[x,y]
[x,y]内每个数乘上
k
k
k
操作2: 格式:
2
2
2
x
x
x
y
y
y
k
k
k 含义:将区间
[
x
,
y
]
[x,y]
[x,y]内每个数加上
k
k
k
操作3: 格式:
3
3
3
x
x
x
y
y
y 含义:输出区间
[
x
,
y
]
[x,y]
[x,y]内每个数的和对
P
P
P取模所得的结果
输出
输出包含若干行整数,即为所有操作 3 3 3的结果。
样例输入
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
样例输出
17
2
样例解释
故输出应为
17
17
17、
2
2
2
(
40
(40
(40
m
o
d
mod
mod
38
=
2
)
38=2)
38=2)
数据范围
对于
30
%
30%
30%的数据:
N
<
=
8
,
M
<
=
10
N<=8,M<=10
N<=8,M<=10
对于
70
%
70%
70%的数据:
N
<
=
1000
,
M
<
=
10000
N<=1000,M<=10000
N<=1000,M<=10000
对于
100
%
100%
100%的数据:
N
<
=
100000
,
M
<
=
100000
N<=100000,M<=100000
N<=100000,M<=100000
(数据已经过加强)
思路
这道题其实和线段树1差不多,就是多了个区间乘值和取模。
于线段树1不同的地方:
- 要多打一个区间乘值的模板。
- 每一次加值或乘值之后就要取模。
- 懒标记的时候乘和加要用不同的变量来装,并且要先乘后加。(重点)
代码
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
ll n,m,p,a[400001],temp,x,y,k;
struct node
{
ll sum,cheng,jia;
}tree[400001];
void up(ll now)//向上递归加值
{
tree[now].sum=(tree[now<<1].sum+tree[now<<1|1].sum)%p;
}
void build(ll now,ll l,ll r)//建树
{
tree[now].cheng=1;
if(l==r)
{
tree[now].sum=a[l];
return ;
}
int mid=(l+r)/2;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
up(now);
}
void down(ll now,ll l,ll r)//懒标记(向下递归)
{
ll ls=now<<1,rs=now<<1|1,mid=(l+r)>>1;
tree[ls].sum=(tree[ls].sum*tree[now].cheng%p+tree[now].jia*(mid-l+1)%p)%p;
tree[ls].cheng=(tree[ls].cheng*tree[now].cheng)%p;
tree[ls].jia=(tree[ls].jia*tree[now].cheng+tree[now].jia)%p;
tree[rs].sum=(tree[rs].sum*tree[now].cheng%p+tree[now].jia*(r-(mid+1)+1)%p)%p;
tree[rs].cheng=(tree[rs].cheng*tree[now].cheng)%p;
tree[rs].jia=(tree[rs].jia*tree[now].cheng+tree[now].jia)%p;
tree[now].jia=0;
tree[now].cheng=1;
return ;
}
void mul(ll now,ll l,ll r,ll x,ll y,ll k)//区间乘值
{
if(r<x||l>y) return ;
if(l>=x&&r<=y)
{
tree[now].sum=(tree[now].sum*k)%p;
tree[now].cheng=(tree[now].cheng*k)%p;
tree[now].jia=(tree[now].jia*k)%p;
return ;
}
down(now,l,r);
ll mid=(l+r)>>1;
mul(now<<1,l,mid,x,y,k);
mul(now<<1|1,mid+1,r,x,y,k);
up(now);
}
void add(ll now,ll l,ll r,ll x,ll y,ll k)//区间加值
{
if(r<x||l>y) return ;
if(l>=x&&r<=y)
{
tree[now].sum=(tree[now].sum+k*(r-l+1)%p)%p;
tree[now].jia=(tree[now].jia+k)%p;
return ;
}
down(now,l,r);
ll mid=(l+r)>>1;
add(now<<1,l,mid,x,y,k);
add(now<<1|1,mid+1,r,x,y,k);
up(now);
}
int answer(ll now,ll l,ll r,ll x,ll y)//区间求和
{
if(r<x||l>y) return 0;
if(l>=x&&r<=y)
return tree[now].sum;
down(now,l,r);
ll mid=(l+r)>>1;
return (answer(now<<1,l,mid,x,y)+answer(now<<1|1,mid+1,r,x,y))%p;
}
int main()
{
scanf("%lld%lld%lld",&n,&m,&p);//读入
for(int i=1;i<=4*n;i++) tree[i].cheng=1;//初始化
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);//读入
build(1,1,n);//建树
while (m)
{
m--;
scanf("%lld",&temp);//读入
if(temp==1)
{
scanf("%lld%lld%lld",&x,&y,&k);//读入
mul(1,1,n,x,y,k);//区间乘值
}
else if(temp==2)
{
scanf("%lld%lld%lld",&x,&y,&k);//读入
add(1,1,n,x,y,k);//区间加值
}
else if(temp==3)
{
scanf("%lld%lld",&x,&y);//读入
printf("%lld\n",answer(1,1,n,x,y)%p);//区间求和
}
}
return 0;
}