【BZOJ】1798 [Ahoi2009]Seq 维护序列seq

来源:互联网 发布:网络语rbq是什么意思 编辑:程序博客网 时间:2024/06/06 04:10

Description

老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。 有长为N的数列,不妨设为a1,a2,,aN 。有如下三种操作形式:
(1)把数列中的一段数全部乘一个值;
(3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值。

Input

第一行两个整数NP(1P1000000000
第二行含有N个非负整数,从左到右依次为a1,a2,,aN,(0ai1000000000,1iN)
第三行有一个整数M,表示操作总数。
从第四行开始每行描述一个操作,输入的操作有以下三种形式:
操作1:“1 t g c”(不含双引号)。表示把所有满足tigai改为ai×c(1tgN,0c1000000000)
操作2:“2 t g c”(不含双引号)。表示把所有满足tigai改为ai+c(1tgN,0c1000000000)
操作3:“3 t g”(不含双引号)。询问所有满足tigai的和模P的值 (1tgN)。 同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。

Output

对每个操作3,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。

Solution

很经典的线段树题目。这题的题点就在于乘法标记和加法标记的叠加。
假如规定一段区间同时有两个标记的时候是先乘后加,假设一段区间,值为a,乘法标记为mul,加法标记为add。
那么其真实值就为a×mul+add
现在来了一个新的加法标记add,那么区间值就是a×mul+add+add
如果来了一个新的乘法标记mul,那么区间值就是a×mul×mul+add×mul
那么这样就很明显了,如果是加法,直接叠加就好,如果是乘法,就把乘法和加法标记同时乘上修改量即可。

#include<stdio.h>#include<cstring>#define M 131072typedef 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 (mid<R) mul(k<<1|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 (mid<R) add(k<<1|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 (mid<R) get(k<<1|1,mid+1,r);}int main(){    scanf("%d%d",&n,&mod);    for (int i=0;i<n;i++) scanf("%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;i<M<<1;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);    }}
0 0
原创粉丝点击