题目描述
老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。
有长为 N 的数列,不妨设为 a1,a2,…,aN。
有如下三种操作形式:
把数列中的一段数全部乘一个值;
把数列中的一段数全部加一个值;
询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模 P 的值。
输入格式
第一行两个整数 N 和 P;
第二行含有 N 个非负整数,从左到右依次为 a1,a2,…,aN;
第三行有一个整数 M,表示操作总数;
从第四行开始每行描述一个操作,输入的操作有以下三种形式:
操作 1:1 t g c,表示把所有满足 t≤i≤g 的 ai 改为 ai×c;
操作 2:2 t g c,表示把所有满足 t≤i≤g 的 ai 改为 ai+c;
操作 3:3 t g,询问所有满足 t≤i≤g 的 ai 的和模 P 的值。
同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。
输出格式
对每个操作 3,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。
数据范围
1≤N,M≤105,
1≤t≤g≤N,
0≤c,ai≤109,
1≤P≤109
输入样例
7 43
1 2 3 4 5 6 7
5
1 2 5 5
3 2 4
2 3 7 9
3 1 3
3 4 7
输出样例
2
35
8
样例解释
初始时数列为 {1,2,3,4,5,6,7};
经过第 1 次操作后,数列为 {1,10,15,20,25,6,7};
对第 2 次操作,和为 10+15+20=45,模 43 的结果是 2;
经过第 3 次操作后,数列为 {1,10,24,29,34,15,16};
对第 4 次操作,和为 1+10+24=35,模 43 的结果是 35;
对第 5 次操作,和为 29+34+15+16=94,模 43 的结果是 8。
题目分析
这是一道线段树加懒标记的模板题。用线段树来维护区间和,并加入两个懒标记add和mul来维护1 2操作即可。
注意: 懒标记向下传递时是先乘在加(这样方便计算)
代码如下
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <stack>
#include <map>
#include <queue>
#include <vector>
#include <set>
#include <algorithm>
#include <iomanip>
#define LL long long
#define PII pair<int,int>
using namespace std;
const int N=1e5+5;
struct Node{
int l,r;
int sum,add,mul; //辅助信息:sum(区间和)
}tr[N*4]; //懒标记:add(给区间加一个数),mul(给区间乘一个数)
int n,m,p;
int a[N];
void pushup(int u)
{ //两个子段的sum相加即为父段的sum
tr[u].sum=(tr[u<<1].sum+tr[u<<1|1].sum)%p;
}
void eval(Node &u,int add,int mul) //将父段的懒标记传递到子段
{
u.add=((LL)u.add*mul+add)%p; //先处理乘法,再处理加法
u.mul=(LL)u.mul*mul%p;
u.sum=((LL)u.sum*mul+(LL)add*(u.r-u.l+1))%p;
}
void pushdown(int u)
{
eval(tr[u<<1],tr[u].add,tr[u].mul); //将父段的懒标记分别传递到两个子段上
eval(tr[u<<1|1],tr[u].add,tr[u].mul);
tr[u].add=0,tr[u].mul=1; //清空父段的懒标记
}
void build(int u,int l,int r) //建树
{
if(l==r) tr[u]={l,r,a[l],0,1};
else
{
tr[u]={l,r,0,0,1};
int mid=l+r>>1;
build(u<<1,l,mid),build(u<<1|1,mid+1,r);
pushup(u);
}
}
void update(int u,int l,int r,int add,int mul) //区间修改
{
if(l<=tr[u].l&&tr[u].r<=r) eval(tr[u],add,mul);
else
{
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid) update(u<<1,l,r,add,mul);
if(mid<r) update(u<<1|1,l,r,add,mul);
pushup(u);
}
}
int query(int u,int l,int r) //区间和查询
{
if(l<=tr[u].l&&tr[u].r<=r) return tr[u].sum;
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
LL sum=0;
if(l<=mid) sum=query(u<<1,l,r);
if(r>mid) sum=(sum+query(u<<1|1,l,r))%p;
return sum;
}
int main()
{
scanf("%d %d",&n,&p);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
build(1,1,n);
scanf("%d",&m);
while(m--)
{
int op,l,r,d;
scanf("%d %d %d",&op,&l,&r);
if(op==1)
{
scanf("%d",&d);
update(1,l,r,0,d);
}
else if(op==2)
{
scanf("%d",&d);
update(1,l,r,d,1);
}
else printf("%d\n",query(1,l,r));
}
return 0;
}