Description
老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。 有长为 N 的数列,不妨设为 a1,a2,…,aN 。有如下三种操作形式:
(1)把数列中的一段数全部乘一个值;
(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,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。
Solution
很经典的线段树题目。这题的题点就在于乘法标记和加法标记的叠加。
假如规定一段区间同时有两个标记的时候是先乘后加,假设一段区间,值为a,乘法标记为mul,加法标记为add。
那么其真实值就为 a×mul+add
现在来了一个新的加法标记 add′ ,那么区间值就是 a×mul+add+add′
如果来了一个新的乘法标记 mul′ ,那么区间值就是 a×mul×mul′+add×mul′
那么这样就很明显了,如果是加法,直接叠加就好,如果是乘法,就把乘法和加法标记同时乘上修改量即可。
#include
#include
#define M 131072
typedef long long ll;
int t[(M<<1)+2],tag[(M<<1)+2],a[(M<<1)+2],c,L,R,aim,ans,n,q,mod;
inline void maintain(const int &k,const int &l,const int &r)
{
if (t[k]==1 && tag[k]==0) return;
a[k]=((ll)a[k]*t[k]+(ll)tag[k]*(r-l+1)) % mod;
if (l==r) {tag[k]=0,t[k]=1;return;}
t[k<<1]=(ll)t[k]*t[k<<1] % mod,t[k<<1|1]=(ll)t[k<<1|1]*t[k] % mod;
tag[k<<1]=(ll)tag[k<<1]*t[k] % mod,tag[k<<1|1]=(ll)tag[k<<1|1]*t[k] % mod;
tag[k<<1]=(tag[k<<1]+tag[k]) % mod,tag[k<<1|1]=(tag[k<<1|1]+tag[k]) % mod;
tag[k]=0,t[k]=1;
}
void mul(const int &k,const int &l,const int &r)
{
maintain(k,l,r);
if (L<=l && r<=R){t[k]=(ll)t[k]*aim % mod;return;}
int mid=(l+r)>>1;
if (L<=mid) mul(k<<1,l,mid);
if (mid1|1,mid+1,r);
maintain(k<<1,l,mid);
maintain(k<<1|1,mid+1,r);
a[k]=(a[k<<1]+a[k<<1|1]) % mod;
}
void add(const int &k,const int &l,const int &r)
{
maintain(k,l,r);
if (L<=l && r<=R){tag[k]=(tag[k]+aim) % mod;return;}
int mid=(l+r)>>1;
if (L<=mid) add(k<<1,l,mid);
if (mid1|1,mid+1,r);
maintain(k<<1,l,mid);
maintain(k<<1|1,mid+1,r);
a[k]=(a[k<<1]+a[k<<1|1]) % mod;
}
void get(const int &k,const int &l,const int &r)
{
maintain(k,l,r);
if (L<=l && r<=R)
{
if (l!=r)
{
int mid=(l+r)>>1;
maintain(k<<1,l,mid);
maintain(k<<1|1,mid+1,r);
a[k]=(a[k<<1]+a[k<<1|1]) % mod;
}
ans=(ans+a[k]) % mod;
return;
}
int mid=(l+r)>>1;
if (L<=mid) get(k<<1,l,mid);
if (midget(k<<1|1,mid+1,r);
}
int main()
{
scanf("%d%d",&n,&mod);
for (int i=0;i"%d",a+M+i),a[M+i]%=mod;
for (int i=M-1;i;i--) a[i]=(a[i<<1]+a[i<<1|1]) % mod;
for (int i=1;i1;i++) t[i]=1;
scanf("%d",&q);
while (q--)
{
scanf("%d%d%d",&c,&L,&R);
if (c==1) scanf("%d",&aim),aim%=mod,mul(1,1,M);
if (c==2) scanf("%d",&aim),aim%=mod,add(1,1,M);
if (c==3) ans=0,get(1,1,M),printf("%d\n",ans);
}
}