UOJ Logo SHYI的博客

博客

[Ahoi2009]Seq 维护序列seq 题解

2016-07-21 20:22:56 By SHYI

题目大意:有长为N的数列,有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值。

思路:用线段树来维护当前的值和要加以及乘的值,由于加与乘是有序的,所以要在做子树之前将标记下传,加和乘分开来、合起来处理都可以。

代码:(当初手抽将1打成l一直RE调了半天才发现)

#include<cstdio>
#include<cstring>
#include<iostream>
#define MAX 400000
#define LL long long
using namespace std;

LL sum[MAX],mul[MAX],add[MAX],mod;

void up_date(int cur)
{
    sum[cur]=(sum[cur<<1]+sum[cur<<1|1])%mod;
}

void creat(int L,int R,int x,int y,int cur)
{
    mul[cur]=1;
    add[cur]=0;
    sum[cur]+=y;
    if (L==R) return;
    int mid=L+R>>1;
    if (x>mid) creat(mid+1,R,x,y,cur<<1|1);
    else creat(L,mid,x,y,cur<<1);
    up_date(cur);
}

void push_down(int cur,int l,int r,int mid)
{
    if (mul[cur]==1 && add[cur]==0) return;
    mul[cur<<1]=mul[cur<<1]*mul[cur]%mod;
    add[cur<<1]=(add[cur<<1]*mul[cur]%mod+add[cur])%mod;
    sum[cur<<1]=(sum[cur<<1]*mul[cur]%mod+add[cur]*(LL)(mid-l+1)%mod)%mod;
    mul[cur<<1|1]=mul[cur<<1|1]*mul[cur]%mod;
    add[cur<<1|1]=(add[cur<<1|1]*mul[cur]%mod+add[cur])%mod;
    sum[cur<<1|1]=(sum[cur<<1|1]*mul[cur]%mod+add[cur]*(LL)(r-mid)%mod)%mod;
    mul[cur]=1;
    add[cur]=0;
    return;
}

void change_mul(int L,int R,int l,int r,int x,int cur)
{
    if (L>=l && R<=r)
    {
        mul[cur]=mul[cur]*(LL)x%mod;
        add[cur]=add[cur]*(LL)x%mod;
        sum[cur]=sum[cur]*(LL)x%mod;
        return;
    }
    int mid=L+R>>1;
    push_down(cur,L,R,mid);
    if (l<=mid) change_mul(L,mid,l,r,x,cur<<1);
    if (r>mid) change_mul(mid+1,R,l,r,x,cur<<1|1);
    up_date(cur);
}

void change_add(int L,int R,int l,int r,int x,int cur)
{
    if (L>=l && R<=r)
    {
        add[cur]=(add[cur]+(LL)x)%mod;
        sum[cur]=(sum[cur]+(LL)(R-L+1)*x%mod)%mod;
        return;
    }
    int mid=L+R>>1;
    push_down(cur,L,R,mid);
    if (l<=mid) change_add(L,mid,l,r,x,cur<<1);
    if (r>mid) change_add(mid+1,R,l,r,x,cur<<1|1);
    up_date(cur);
}

LL ask(int L,int R,int l,int r,int cur)
{
    if (L>=l && R<=r) return sum[cur];
    int mid=L+R>>1;
    LL ans=0;
    push_down(cur,L,R,mid);
    if (l<=mid) ans=(ans+ask(L,mid,l,r,cur<<1))%mod;
    if (r>mid) ans=(ans+ask(mid+1,R,l,r,cur<<1|1))%mod;
    up_date(cur);
    return ans;
}

int main()
{
    int n,m,a,b,c,i,x;
    scanf("%d%lld",&n,&mod);
    for (i=1;i<=n;i++) scanf("%d",&a),creat(1,n,i,a%mod,1);
    scanf("%d",&m);
    for (i=1;i<=m;i++)
    {
        scanf("%d",&x);
        if (x==1) scanf("%d%d%d",&a,&b,&c),change_mul(1,n,a,b,c%mod,1);
        if (x==2) scanf("%d%d%d",&a,&b,&c),change_add(1,n,a,b,c%mod,1);
        if (x==3) scanf("%d%d",&a,&b),printf("%lld\n",ask(1,n,a,b,1));
    }
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。