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];
struct Tree{
    int left,right;
    ll data,lazy,mul;
}tree[2000010];
void build(int p,int left,int right){
    tree[p].left=left;
    tree[p].right=right;
    tree[p].mul=1;
    if(left==right){tree[p].data=a[left];return;}
    build(p<<1,left,(left+right)>>1);
    build(p<<1|1,((left+right)>>1)+1,right);
    tree[p].data=(tree[p<<1].data+tree[p<<1|1].data)%mod;
}
void pushdown(int p){
    ll mul=tree[p].mul,lazy=tree[p].lazy;
    tree[p<<1].lazy*=mul;
    tree[p<<1].lazy+=lazy;tree[p<<1].lazy%=mod;
    tree[p<<1].mul*=mul;tree[p<<1].mul%=mod;
    tree[p<<1|1].lazy*=mul;
    tree[p<<1|1].lazy+=lazy;tree[p<<1|1].lazy%=mod;
    tree[p<<1|1].mul*=mul;tree[p<<1|1].mul%=mod;
    tree[p].data*=tree[p].mul;
    tree[p].data+=(tree[p].right-tree[p].left+1)*tree[p].lazy;
    tree[p].data%=mod;
    tree[p].lazy=0;tree[p].mul=1;
}
void add(int left,int right,ll k,int p){
    int l=tree[p].left,r=tree[p].right;
    if(l>right||r<left||p>4*n)return;
    pushdown(p);
    if(l>=left&&r<=right){
        tree[p].lazy+=k;
        tree[p].lazy%=mod;
        return;
    }
    tree[p].data+=k*(min(right,r)-max(left,l)+1);
    tree[p].data%=mod;
    add(left,right,k,p<<1);
    add(left,right,k,p<<1|1);
}
ll multy(int left,int right,ll k,int p){
    int l=tree[p].left,r=tree[p].right;
    if(l>right||r<left||p>4*n)return 0;
    pushdown(p);
    if(l>=left&&r<=right){
        ll temp=tree[p].data*tree[p].mul+tree[p].lazy*(r-l+1);
        tree[p].lazy*=k;tree[p].lazy%=mod;
        tree[p].mul*=k;tree[p].mul%=mod;
        return ((k-1)*temp%mod+mod)%mod;//非常重要!!!!!!
    }
    ll temp=multy(left,right,k,p<<1)+multy(left,right,k,p<<1|1);
    tree[p].data+=temp;
    tree[p].data=;
    return temp;
}
ll query(int left,int right,int p){
    int l=tree[p].left,r=tree[p].right;
    if(l>right||r<left||p>4*n)return 0;
    pushdown(p);
    if(l>=left&&r<=right)return tree[p].data;
    return query(left,right,p<<1)+query(left,right,p<<1|1);
}
int main(){
    int s,x,y,i;
    scanf("%d%lld",&n,&mod);
    for(i=1;i<=n;i++)scanf("%lld",&a[i]);
    build(1,1,n);
    scanf("%d",&m);
    while(m--){
        scanf("%d%d%d",&s,&x,&y);
        if(s-3){
            scanf("%lld",&k);
            if(s-1)add(x,y,k,1);
            else multy(x,y,k,1);
        }else printf("%lld\n",query(x,y,1)%mod);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Potassium/p/10240855.html

时间: 2024-10-14 09:47:17

P2023 [AHOI2009]维护序列 题解(线段树的相关文章

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

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

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

BZOJ_1798_[AHOI2009]维护序列_线段树

题意:老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,-,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值. 分析:线段树上要打两个标记.要注意下传的顺序.显然先乘后加和先加后乘是不一样的.我们发现如果是先加后乘的话更改子树值的式子里会出现除法.不妨规定任何时候都先乘后加.推出的式子即为 t[lson]=(t[lso

洛谷 P2023 [AHOI2009]维护序列

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

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).第三行有一个整

bzoj 1798: [Ahoi2009]Seq 维护序列seq 线段树 区间乘法区间加法 区间求和

1798: [Ahoi2009]Seq 维护序列seq Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://www.lydsy.com/JudgeOnline/problem.php?id=1798 Description 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列

bzoj1798: [Ahoi2009]Seq 维护序列seq 线段树

题目传送门 这道题就是线段树 先传乘法标记再传加法 #include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; const int M=400010; LL read(){ LL ans=0,f=1,c=getchar(); while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}

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

题意:链接 方法:线段树 解析: 俩标记sb题 更新乘的时候更新加 完了 代码: #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 #define N 100010 using namespace std; typedef