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,int temp)
{
    lazy1[temp]=1;lazy2[temp]=0;
    if(l==r)
    {
        tree[temp]=a[l]%mod;
        return;
    }
    int mid=(l+r)/2;
    build(l,mid,temp*2);
    build(mid+1,r,temp*2+1);
    tree[temp]=(tree[temp*2]+tree[temp*2+1])%mod;
}
void pushdown(int l,int r,int temp)
{
    //lazy1[temp*2]=a0,lazy2[temp*2]=b0,lazy1[temp]=a
    //a0*x+b0->a*(a0*x+b0)->a*a0*x+a*b0
    tree[temp*2]=(tree[temp*2]*lazy1[temp]+l*lazy2[temp])%mod;
    tree[temp*2+1]=(tree[temp*2+1]*lazy1[temp]+r*lazy2[temp])%mod;
    lazy1[temp*2]=(lazy1[temp*2]*lazy1[temp])%mod;
    lazy1[temp*2+1]=(lazy1[temp*2+1]*lazy1[temp])%mod;
    lazy2[temp*2]=(lazy2[temp*2]*lazy1[temp]+lazy2[temp])%mod;
    lazy2[temp*2+1]=(lazy2[temp*2+1]*lazy1[temp]+lazy2[temp])%mod;
    lazy1[temp]=1;lazy2[temp]=0;
}
void add1(int l,int r,int p,int q,LL c,int temp)
{
    if(p<=l&&r<=q)
    {
        tree[temp]=(tree[temp]*c)%mod;
        lazy1[temp]=(lazy1[temp]*c)%mod;
        lazy2[temp]=(lazy2[temp]*c)%mod;//注意这里,乘的发生改变加的就要改变
        return;
    }
    int mid=(l+r)/2;
    pushdown(mid-l+1,r-mid,temp);
    if(mid>=p)add1(l,mid,p,q,c,temp*2);
    if(mid<q)add1(mid+1,r,p,q,c,temp*2+1);
    tree[temp]=(tree[temp*2]+tree[temp*2+1])%mod;
}
void add2(int l,int r,int p,int q,LL c,int temp)
{
    if(p<=l&&r<=q)
    {
        tree[temp]=(tree[temp]+(r-l+1)*c)%mod;
        lazy2[temp]=(lazy2[temp]+c)%mod;
        return;
    }
    int mid=(l+r)/2;
    pushdown(mid-l+1,r-mid,temp);
    if(mid>=p)add2(l,mid,p,q,c,temp*2);
    if(mid<q)add2(mid+1,r,p,q,c,temp*2+1);
    tree[temp]=(tree[temp*2]+tree[temp*2+1])%mod;
}
LL get(int l,int r,int p,int q,int temp)
{
    if(p<=l&&r<=q)return tree[temp];
    int mid=(l+r)/2;
    LL res=0;
    pushdown(mid-l+1,r-mid,temp);
    if(mid>=p)res=(res+get(l,mid,p,q,temp*2))%mod;
    if(mid<q)res=(res+get(mid+1,r,p,q,temp*2+1))%mod;
    return res;
}
int main()
{
    scanf("%d %lld",&n,&mod);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    build(1,n,1);
    int m,s,t,g;
    LL c;
    scanf("%d",&m);
    while(m--)
    {
        scanf("%d",&s);
        if(s==1)
        {
            scanf("%d%d%lld",&t,&g,&c);
            add1(1,n,t,g,c,1);
        }
        else if(s==2)
        {
            scanf("%d%d%lld",&t,&g,&c);
            add2(1,n,t,g,c,1);
        }
        else
        {
            scanf("%d%d",&t,&g);
            LL ans=get(1,n,t,g,1);
            printf("%lld\n",ans);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/HooYing/p/11247399.html

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

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

线段树区间修改+查询区间和

1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e5+10; 4 int a[maxn]; 5 int sum[maxn<<2],exc[maxn<<2]; 6 void maintain(int k) 7 { 8 sum[k]=sum[k<<1]+sum[k<<1|1]; 9 } 10 void pushdown(int lenl,int lenr,int k

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];

hdu 4902 Nice boat(线段树区间修改,输出最终序列)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4902 Problem Description There is an old country and the king fell in love with a devil. The devil always asks the king to do some crazy things. Although the king used to be wise and beloved by his peopl

线段树区间修改与查询

单点修改与查询 //单点修改,区间询问最小值 #include <iostream> #include <cstdio> #define maxn 101 #define INF 0x7fffffff using namespace std; int a[maxn],n,m; int mi[maxn]; void build(int k,int l,int r)//k是当前节点编号,l,r为当前节点代表区间 { if(l==r) { mi[k]=a[l]; return; } in

线段树区间修改模板

本来打算把大白书第三章一口气攻下来的,但是这个线段树也是卡了好久. 不敢过题太快,怕自己走马观花到头来结果什么都不会. 可也不能再拖了,在做题中也许有更多的体会. 模板一:1 L R v 表示区间[L, R]所有元素都加上v2 L R   表示查询区间[L, R]的sum, min, maxsumv[o]的定义为:如果只执行节点o及其子孙节点的中的add操作,节点o对应区间中所有数之和 1 //线段树区间修改 2 //1 L R v 表示区间[L, R]所有元素都加上v 3 //2 L R 表示

UVA11992 - Fast Matrix Operations ( 线段树 + 区间修改 + 好题 )

UVA11992 - Fast Matrix Operations ( 线段树 + 区间修改 + 好题 ) 这是大白书上的例题,一直放着没有去A掉,这是一道线段树区间修改的好题. 线段树中需要维护三个域 ,max, min, sum,也就是区间最大值,最小值,区间和 题目大意: r 行 c 列 的全0矩阵,支持三个操作 1 x1 y1 x2 y2 v 子矩阵(x1,y1,x2,y2)的所有元素增加v 2 x1 y1 x2 y2 v 子矩阵(x1,y1,x2,y2)的所有元素设为v 3 x1 y1

poj 2777 Count Color(线段树区间修改)

题目链接:http://poj.org/problem?id=2777 题目意思:就是问你在询问的区间里有几种不同的颜色 思路:这题和一般的区间修改差不多,但是唯一不同的就是我们要怎么计算有种颜色,所以这时候我们就需要把延时标记赋予不同的意义,当某段区间有多种颜色时就赋值为-1,当为一种颜色时就把它赋值为这个颜色的号数.这儿我们要怎么统计询问区间不同的颜色数叻,为了不重复计算同一种颜色,那么我们就需要用一个数组来标记计算过的颜色,当我们下次遇到时就不需要再次计算了.... 代码核心处就在计数那儿

hdu-5023 A Corrupt Mayor&#39;s Performance Art (线段树区间修改)

今天集训队打比赛的一道题,很明显是个线段树,我们队照着lrj蓝书敲了一通,机智的将修改值和加和改成了位运算:|= 但是好像哪里出了点小问题,就是不对,赛后又水了一遍,竟然过了...发现还是lrj的书好啊,市面上的模板一点也不好用,连区间修改都没有 . 等集训完了要静心好好系统的学习一下线段树 . 多看多刷lrj的书 . 细节参见代码: #include<bits/stdc++.h> using namespace std; const int maxn = 1000000 + 5; int n