【AHOI2009】bzoj1798 维护序列

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,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。

典型的线段树区间修改区间询问。
注意两个标记的顺序问题,即把每个结点强制看成x*m1+m2,这样的话标记乘法要同时标记加法,下传时也要先传乘法。
注意有乘0的情况。

#include<cstdio>
#include<cstring>
#define LL long long
int m_p[1000010],m_t[1000010],sum[1000010],mod,n;
void down(int p,int L,int R)
{
    sum[p]=(LL)sum[p]*m_t[p]%mod;
    m_p[p*2]=(LL)m_p[p*2]*m_t[p]%mod;
    m_t[p*2]=(LL)m_t[p*2]*m_t[p]%mod;
    m_p[p*2+1]=(LL)m_p[p*2+1]*m_t[p]%mod;
    m_t[p*2+1]=(LL)m_t[p*2+1]*m_t[p]%mod;
    sum[p]=(sum[p]+(LL)(R-L+1)*m_p[p])%mod;
    m_p[p*2]=(m_p[p*2]+m_p[p])%mod;
    m_p[p*2+1]=(m_p[p*2+1]+m_p[p])%mod;
    m_t[p]=1;
    m_p[p]=0;
}
void add(int p,int L,int R,int l,int r,int x)
{
    down(p,L,R);
    int i,j,k,M=(L+R)/2;
    if (L==l&&R==r)
    {
        m_p[p]=(m_p[p]+x)%mod;
        return;
    }
    if (r<=M)
      add(p*2,L,M,l,r,x);
    else
    {
        if (l>M)
          add(p*2+1,M+1,R,l,r,x);
        else
        {
            add(p*2,L,M,l,M,x);
            add(p*2+1,M+1,R,M+1,r,x);
        }
    }
    down(p*2,L,M);
    down(p*2+1,M+1,R);
    sum[p]=(sum[p*2]+sum[p*2+1])%mod;
}
void multi(int p,int L,int R,int l,int r,int x)
{
    down(p,L,R);
    int i,j,k,M=(L+R)/2;
    if (L==l&&R==r)
    {
        m_p[p]=((LL)m_p[p]*x)%mod;
        m_t[p]=((LL)m_t[p]*x)%mod;
        return;
    }
    if (r<=M)
      multi(p*2,L,M,l,r,x);
    else
    {
        if (l>M)
          multi(p*2+1,M+1,R,l,r,x);
        else
        {
            multi(p*2,L,M,l,M,x);
            multi(p*2+1,M+1,R,M+1,r,x);
        }
    }
    down(p*2,L,M);
    down(p*2+1,M+1,R);
    sum[p]=(sum[p*2]+sum[p*2+1])%mod;
}
int qry(int p,int L,int R,int l,int r)
{
    down(p,L,R);
    int i,j,k,M=(L+R)/2;
    if (L==l&&R==r)
      return sum[p];
    if (r<=M)
      return qry(p*2,L,M,l,r);
    if (l>M)
      return qry(p*2+1,M+1,R,l,r);
    return (qry(p*2,L,M,l,M)+qry(p*2+1,M+1,R,M+1,r))%mod;
}
int main()
{
    int i,j,k,m,n,x,y,z,w;
    scanf("%d%d",&n,&mod);
    for (i=1;i<=4*n;i++)
      m_t[i]=1;
    for (i=1;i<=n;i++)
    {
        scanf("%d",&x);
        add(1,1,n,i,i,x);
    }
    scanf("%d",&m);
    while (m--)
    {
        scanf("%d%d%d",&x,&y,&z);
        if (x==1)
        {
            scanf("%d",&w);
            multi(1,1,n,y,z,w);
        }
        if (x==2)
        {
            scanf("%d",&w);
            add(1,1,n,y,z,w);
        }
        if (x==3) printf("%d\n",qry(1,1,n,y,z));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值