题目
Time Limit: 30 Sec
Memory Limit: 64 MB
Description
老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。 有长为N的数列,不妨设为a1,a2,…,aN 。有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值。
Input
第一行两个整数N和P(1≤P≤1000000000)。第二行含有N个非负整数,从左到右依次为a1,a2,…,aN, (0≤ai≤1000000000,1≤i≤N)。第三行有一个整数M,表示操作总数。从第四行开始每行描述一个操作,输入的操作有以下三种形式: 操作1:“1 t g c”(不含双引号)。表示把所有满足t≤i≤g的ai改为ai×c (1≤t≤g≤N,0≤c≤1000000000)。 操作2:“2 t g c”(不含双引号)。表示把所有满足t≤i≤g的ai改为ai+c (1≤t≤g≤N,0≤c≤1000000000)。 操作3:“3 t g”(不含双引号)。询问所有满足t≤i≤g的ai的和模P的值 (1≤t≤g≤N)。 同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。
Output
对每个操作3,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。
Sample Input
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
Sample Output
2
35
8
HINT
【样例说明】
初始时数列为(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。
测试数据规模如下表所示
数据编号 1 2 3 4 5 6 7 8 9 10
N= 10 1000 1000 10000 60000 70000 80000 90000 100000 100000
M= 10 1000 1000 10000 60000 70000 80000 90000 100000 100000
题解
- 裸线段树+特殊乘法操作
- 乘法和加法各一个标记
- 更新的时候,如果是加操作,直接套用裸线段树的模式
如果是乘操作,则既要把乘的标记乘v,加的标记也要乘v,简单说就是先乘后加,原理是乘法分配律
(a+b)*v=a*v+b*v,其中a是原数,b是加的标记,v是此时乘的数 - pushdown时并起来就好了
- 另外记得随时取模
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int M=100010;
int n,p,m;
struct node{
int L,R;
long long sum,c1,c2;//c1 >- * c2 >- +
}t[M*4+5];
long long x;
void pushup(int pos){
t[pos].sum=(t[pos<<1].sum+t[pos<<1|1].sum)%p;
}
void build(int pos,int L,int R){
t[pos].L=L;
t[pos].R=R;
t[pos].sum=t[pos].c2=0;
t[pos].c1=1;//初始化为1
if(L==R){
scanf("%lld",&x);
t[pos].sum=(x%p);
}
else{
int mid=(L+R)>>1;
build(pos<<1,L,mid);
build(pos<<1|1,mid+1,R);
pushup(pos);
}
}
void pushdown(int pos){
if(t[pos].c1==1&&t[pos].c2==0) return;
int L1=t[pos<<1].R-t[pos<<1].L+1;
int L2=t[pos<<1|1].R-t[pos<<1|1].L+1;
t[pos<<1].c1=(t[pos].c1*t[pos<<1].c1)%p;
t[pos<<1|1].c1=(t[pos].c1*t[pos<<1|1].c1)%p;
t[pos<<1].c2=(t[pos].c1*t[pos<<1].c2+t[pos].c2)%p;
t[pos<<1|1].c2=(t[pos].c1*t[pos<<1|1].c2+t[pos].c2)%p;
t[pos<<1].sum=(t[pos<<1].sum*t[pos].c1+L1*t[pos].c2)%p;
t[pos<<1|1].sum=(t[pos<<1|1].sum*t[pos].c1+L2*t[pos].c2)%p;
t[pos].c1=1;
t[pos].c2=0;
}
void update1(int pos,int ll,int rr,int v){
int L=t[pos].L;
int R=t[pos].R;
if(ll>R||rr<L) return;
if(ll<=L&&rr>=R){
t[pos].c1=(t[pos].c1*v)%p;
t[pos].c2=(t[pos].c2*v)%p;
t[pos].sum=(t[pos].sum*v)%p;
return;
}
pushdown(pos);
update1(pos<<1,ll,rr,v);
update1(pos<<1|1,ll,rr,v);
pushup(pos);
}
void update2(int pos,int ll,int rr,int v){
int L=t[pos].L;
int R=t[pos].R;
if(ll>R||rr<L) return;
if(ll<=L&&rr>=R){
t[pos].c2=(t[pos].c2+v)%p;
t[pos].sum=(t[pos].sum+(R-L+1)*v)%p;
return;
}
pushdown(pos);
update2(pos<<1,ll,rr,v);
update2(pos<<1|1,ll,rr,v);
pushup(pos);
}
long long query(int pos,int ll,int rr){
int L=t[pos].L;
int R=t[pos].R;
if(ll>R||rr<L) return 0;
if(ll<=L&&rr>=R) return t[pos].sum%p;
pushdown(pos);
return (query(pos<<1,ll,rr)+query(pos<<1|1,ll,rr))%p;
}
int main(){
scanf("%d%d",&n,&p);
build(1,1,n);
scanf("%d",&m);
int op,a,b,z;
for(int i=1;i<=m;++i){
scanf("%d",&op);
if(op==1){
scanf("%d%d%d",&a,&b,&z);
update1(1,a,b,z);
}
if(op==2){
scanf("%d%d%d",&a,&b,&z);
update2(1,a,b,z);
}
if(op==3){
scanf("%d%d",&a,&b);
printf("%lld\n",query(1,a,b)%p);
}
}
return 0;
}