BZOJ 1798 [Ahoi2009]Seq 维护序列——线段树

1798: [Ahoi2009]Seq 维护序列seq

题目描述
老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。 有长为N的数列,不妨设为a1,a2,…,aN 。有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值。
输入
第一行两个整数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)。 同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。
输出
对每个操作3,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。
解题思路
这题是很裸的线段树的题目,就是又有加又有乘,于是线段树就有了两个tag,传tag的时候稍微推一下。这道题我A了很久,半个月左右QAQ,最主要线段树没理解好,记住:传到x点tag的同时修正x点。嗯,这个很重要。

#include<cstdio>
#define LL long long
using namespace std;
const int maxn=100005;
struct jz{
    int L,R;
    LL tag1,tag2,w;
}a[4*maxn];
inline int _read(){
    int num=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') num=num*10+ch-48,ch=getchar();
    return num;
}
int tt,n,m,w[maxn];
void build(int x,int L,int R){
    a[x].L=L;a[x].R=R;a[x].tag1=1;
    if (L==R){a[x].w=(LL)w[L]%tt;return;}
    int mid=L+R>>1;
    build(x*2,L,mid);build(x*2+1,mid+1,R);
    a[x].w=(a[x*2].w+a[x*2+1].w)%tt;
}
void Pushdown(int x){
    if (a[x].L==a[x].R) return;
    int tag1=a[x].tag1,tag2=a[x].tag2;a[x].tag1=1;a[x].tag2=0;
    a[x*2].w=(a[x*2].w*tag1+(LL)tag2*(a[x*2].R-a[x*2].L+1))%tt;
    a[x*2+1].w=(a[x*2+1].w*tag1+(LL)tag2*(a[x*2+1].R-a[x*2+1].L+1))%tt;
    a[x*2].tag1=(a[x*2].tag1*tag1)%tt;
    a[x*2+1].tag1=(a[x*2+1].tag1*tag1)%tt;
    a[x*2].tag2=(a[x*2].tag2*tag1+tag2)%tt;
    a[x*2+1].tag2=(a[x*2+1].tag2*tag1+tag2)%tt;
}
void change(int x,int L,int R,int c,int p){
    if (a[x].L==L&&a[x].R==R){
        a[x].tag1=(a[x].tag1*c)%tt;
        a[x].tag2=(a[x].tag2*c+p)%tt;
        a[x].w=(a[x].w*c+(a[x].R-a[x].L+1)*p)%tt;
        return;
    }
    Pushdown(x);
    int mid=a[x].L+a[x].R>>1;
    if (R<=mid) change(x*2,L,R,c,p);
    else if (L>mid) change(x*2+1,L,R,c,p);
    else{change(x*2,L,mid,c,p);change(x*2+1,mid+1,R,c,p);}
    a[x].w=(a[x*2].w+a[x*2+1].w)%tt;
}
int query(int x,int L,int R){
    if (a[x].L==L&&a[x].R==R) return a[x].w;
    Pushdown(x);
    int mid=a[x].L+a[x].R>>1;
    if (R<=mid) return query(x*2,L,R);
    else if (L>mid) return query(x*2+1,L,R);
    else return (query(x*2,L,mid)+query(x*2+1,mid+1,R))%tt;
}
int main(){
    freopen("exam.in","r",stdin);
    freopen("exam.out","w",stdout);
    n=_read();tt=_read();
    for (int i=1;i<=n;i++) w[i]=_read();
    build(1,1,n);
    m=_read();
    while (m--){
        int t=_read(),x=_read(),y=_read(),z;
        if (t==1){z=_read();change(1,x,y,z,0);}
        if (t==2){z=_read();change(1,x,y,1,z);}
        if (t==3) printf("%d\n",query(1,x,y));
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值