【线段树】Bzoj1798 [AHOI2009] 维护序列

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

Sample Input

7 43
1 2 3 4 5 6 7
5
1 2 5 5
3 2 4
2 3 7 9
3 1 3
3 4 7

Sample Output

2
35
8

题解

三个数组sum, mul, add,具体操作看代码

要注意前面操作对当前的影响,及时pushback,统计祖先add&mul也要想清

以后做这种题目都要想清定义,每一步的影响,不然就都乱了

以后要自己想清造小数据过了后再去搞大数据拍,自己造的数据密度大很多的,而且先要保证正确性才行

代码

非常不愉快,打完后找标程对拍,大数据不一样,调了好久,结果是标程有问题QwQ

以后全部用ll保平安啦

#include<cstdio>
#define ll long long
const int maxn=4e5+5;

ll sumv[maxn],mulv[maxn],addv[maxn];
ll N,M,mod,p,q,w,x;

void pushup(ll o,ll l,ll r){
    sumv[o]=0;
    if(l<r) sumv[o]=sumv[o*2]+sumv[o*2+1];
    sumv[o]*=mulv[o],sumv[o]%=mod;
    sumv[o]+=(r-l+1)*addv[o],sumv[o]%=mod;
}

void pushdown(ll o,ll l,ll r){
    ll Mul=mulv[o],Add=addv[o],lc=o*2,rc=o*2+1,mid=(l+r)>>1;
    sumv[lc]=(sumv[lc]*Mul+(mid-l+1)*Add)%mod;
    sumv[rc]=(sumv[rc]*Mul+(r-mid)*Add)%mod;
    mulv[lc]=(mulv[lc]*Mul)%mod;
    mulv[rc]=(mulv[rc]*Mul)%mod;
    addv[lc]=(addv[lc]*Mul+Add)%mod;
    addv[rc]=(addv[rc]*Mul+Add)%mod;
    mulv[o]=1;addv[o]=0;
}

void add(ll o,ll l,ll r){
    if(p<=l&&q>=r) addv[o]+=w,addv[o]%=mod;
    else{
        pushdown(o,l,r);
        int mid=(l+r)/2;
        if(p<=mid) add(o*2,l,mid);
        if(q>mid) add(o*2+1,mid+1,r);
    }
    pushup(o,l,r);
}

void mul(ll o,ll l,ll r){
    if(p<=l&&q>=r){
        mulv[o]*=w,mulv[o]%=mod;
        addv[o]*=w,addv[o]%=mod;
    }
    else{
        pushdown(o,l,r);
        int mid=(l+r)/2;
        if(p<=mid) mul(o*2,l,mid);
        if(q>mid) mul(o*2+1,mid+1,r);
    }
    pushup(o,l,r);
}

ll ret;
void sum(ll o,ll l,ll r,ll Add,ll Mul){
    if(p<=l&&q>=r){
        ret+=Mul*sumv[o]+Add*(r-l+1);
        ret%=mod;
    }
    else{
        int mid=(l+r)/2;
        if(p<=mid) sum(o*2,l,mid,(Add+Mul*addv[o])%mod,Mul*mulv[o]%mod);
        if(q>mid) sum(o*2+1,mid+1,r,(Add+Mul*addv[o])%mod,Mul*mulv[o]%mod);
    }
}

int main(){
    scanf("%lld%lld",&N,&mod);
    for(int i=1;i<=3*N;i++) mulv[i]=1;

    for(int i=1;i<=N;i++){
        scanf("%lld",&w);
        p=q=i;
        add(1,1,N);
    }

    scanf("%lld",&M);
    for(int i=1;i<=M;i++){
        scanf("%lld",&x);
        if(x==1){
            scanf("%lld%lld%lld",&p,&q,&w);
            mul(1,1,N);
        }
        else if(x==2){
            scanf("%lld%lld%lld",&p,&q,&w);
            add(1,1,N);
        }
        else{
            scanf("%lld%lld",&p,&q);
            ret=0;
            sum(1,1,N,0,1);
            printf("%lld\n",ret);
            continue;
        }
    }

    return 0;
}
时间: 2024-10-16 01:39:36

【线段树】Bzoj1798 [AHOI2009] 维护序列的相关文章

「线段树」[AHOI2009]维护序列

双倍经验,还是蓝题,岂不美哉 题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为\(a_1,a_2,-,a_N\) .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值. 输入格式 第一行两个整数\(N\)和\(P\)\((1≤P≤1000000000)\). 第二行含有\(N\)个非负整数,从左到右依次为\(a_1,a_2,-,

P2023 [AHOI2009]维护序列 题解(线段树

题目链接 P2023 [AHOI2009]维护序列 解题思路 线段树板子.不难,但是...有坑.坑有多深?一页\(WA\). 由于乘法可能乘\(k=0\),我这种做法可能会使结果产生负数.于是就有了这篇题解. (详情见代码注释) AC代码 #include<stdio.h> #define min(a,b) (a>b?b:a) #define max(a,b) (a>b?a:b) typedef long long ll; int n,m; ll mod,k,a[500010];

洛谷 P2023 [AHOI2009]维护序列

P2023 [AHOI2009]维护序列 题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值. 输入输出格式 输入格式: 第一行两个整数N和P(1≤P≤1000000000). 第二行含有N个非负整数,从左到右依次为a1,a2,…,aN, (0≤ai≤100

BZOJ 1798 [Ahoi2009]维护序列seq (线段树)

题意 对于一个给定的序列有3种操作: 1.给一个区间的数乘c 2.给一个区间的数加c 3.查询区间和. 思路 就是普通的线段树区间更新,因为更新操作有两种,维护两个延迟标记就可以了,不过要注意乘和加在更新时相互之间的关系,在更新乘的时候之前加的数也要相应的乘,更新加的时候之前所乘的数没有改变. 代码 #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm>

P2023 [AHOI2009]维护序列 --线段树

题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值. 输入输出格式 输入格式: 第一行两个整数N和P(1≤P≤1000000000).第二行含有N个非负整数,从左到右依次为a1,a2,…,aN, (0≤ai≤1000000000,1≤i≤N).第三行有一个整

P2023 [AHOI2009] 维护序列(线段树水题)

题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值. 输入输出格式 输入格式: 第一行两个整数N和P(1≤P≤1000000000).第二行含有N个非负整数,从左到右依次为a1,a2,…,aN, (0≤ai≤1000000000,1≤i≤N).第三行有一个整

P2023 [AHOI2009]维护序列 (线段树区间修改查询)

题目链接:https://www.luogu.org/problemnew/show/P2023 一道裸的线段树区间修改题,懒惰数组注意要先乘后加 #include<bits/stdc++.h> using namespace std; typedef long long LL; const int maxx = 400010; LL tree[maxx],lazy1[maxx],lazy2[maxx],a[maxx],mod; int n; void build(int l,int r,in

POJ P2828 Buy Ticket——线段树的其他信息维护

Description Railway tickets were difficult to buy around the Lunar New Year in China, so we must get up early and join a long queue… The Lunar New Year was approaching, but unluckily the Little Cat still had schedules going here and there. Now, he ha

[AHOI2009]维护序列

OJ题号:洛谷2023.BZOJ1798 思路: 参见[洛谷3373][模板]线段树 2 1 #include<cstdio> 2 #include<cctype> 3 #include<cstring> 4 #include<cstdlib> 5 #include<algorithm> 6 using namespace std; 7 #define ll long long 8 #define maxn 100001 9 #define ro