[AHOI2009]维护序列

题解:

看到这种题目,一看就是线段树

很明显的区间线段树

加法反正不用说

乘法的时候往加法的地方也要翻倍

代码:

#include<bits/stdc++.h> 
using namespace std;
typedef long long ll;
const int N=400005;
ll p,v,sum_,addv[N],mulv[N],sumv[N],a[N];
int n,m,c,x,y;
void pushdown(int o,int l,int l1,int r,int r1)
{
    sumv[o*2]=(sumv[o*2]*mulv[o]+addv[o]*(l1-l+1))%p;
    sumv[o*2+1]=(sumv[o*2+1]*mulv[o]+addv[o]*(r1-r+1))%p;
    addv[o*2]=(addv[o*2]*mulv[o]+addv[o])%p;
    addv[o*2+1]=(addv[o*2+1]*mulv[o]+addv[o])%p;
    mulv[o*2]=(mulv[o*2]*mulv[o])%p;
    mulv[o*2+1]=(mulv[o*2+1]*mulv[o])%p;
    mulv[o]=1;
    addv[o]=0;
}
void updatemul(int o,int l,int r)
{    
    if(x<=l&&r<=y)
     {
        mulv[o]=(mulv[o]*v)%p;
        addv[o]=(addv[o]*v)%p;
        sumv[o]=(sumv[o]*v)%p;
        return ;
     }
    int mid=(l+r)>>1;
    pushdown(o,l,mid,mid+1,r);
    if(x<=mid)updatemul(o*2,l,mid);
    if(y>mid)updatemul(o*2+1,mid+1,r);
    sumv[o]=(sumv[o*2]+sumv[o*2+1])%p;
}
void updateadd(int o,int l,int r)
{
    if(x<=l&&r<=y)
     {
        addv[o]=(addv[o]+v)%p;
        sumv[o]=(sumv[o]+v*(r-l+1))%p;
        return ;
     }
    int mid=(l+r)>>1;
    pushdown(o,l,mid,mid+1,r);
    if(x<=mid)updateadd(o*2,l,mid);
    if(y>mid)updateadd(o*2+1,mid+1,r);
    sumv[o]=(sumv[o*2]+sumv[o*2+1])%p;
}
void create(int o,int l,int r)
{
    addv[o]=0;
    mulv[o]=1;
    if(l==r)
     {
        sumv[o]=a[l];
        return ;
     }
    int mid=(l+r)>>1;
    create(o*2,l,mid);
    create(o*2+1,mid+1,r);
    sumv[o]=(sumv[o*2]+sumv[o*2+1])%p;
}
void query(int o,int l,int r)
{
    if(x<=l&&r<=y)
     {
        sum_+=sumv[o]%p;
        sum_%=p;
        return ;
     }
    int mid=(l+r)>>1;
    pushdown(o,l,mid,mid+1,r);
    if(x<=mid)query(o*2,l,mid);
    if(y>mid)query(o*2+1,mid+1,r);
    sumv[o]=(sumv[o*2]+sumv[o*2+1])%p;
}
int main()
{
    scanf("%d%d",&n,&p);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
    create(1,1,n);
    scanf("%d",&m);
    while (m--)
     {
        scanf("%d%d%d",&c,&x,&y);
        if(c==1)
         {
            scanf("%d",&v);
            updatemul(1,1,n);
         }
        if(c==2)
         {
            scanf("%d",&v);
            updateadd(1,1,n);
         }
        if(c==3)
         {
            sum_=0;
            query(1,1,n);
            printf("%lld\n",sum_);
         }
     }
    return 0;
}

 

转载于:https://www.cnblogs.com/xuanyiming/p/7705624.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值