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

双倍经验,还是蓝题,岂不美哉

题目描述

老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。 有长为N的数列,不妨设为\(a_1,a_2,…,a_N\) 。有如下三种操作形式:
(1)把数列中的一段数全部乘一个值;
(2)把数列中的一段数全部加一个值;
(3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值。

输入格式

第一行两个整数\(N\)和\(P\)\((1≤P≤1000000000)\)。
第二行含有\(N\)个非负整数,从左到右依次为\(a_1,a_2,…,a_N\), \((0≤a_i≤1000000000,1≤i≤N)\)。
第三行有一个整数\(M\),表示操作总数。
从第四行开始每行描述一个操作,输入的操作有以下三种形式:
操作1:“1 t g c”(不含双引号)。表示把所有满足\(t≤i≤g\)的\(a_i\)改为\(a_i×c\)\((1≤t≤g≤N,0≤c≤1000000000)\)。
操作2:“2 t g c”(不含双引号)。表示把所有满足\(t≤i≤g\)的\(a_i\)改为\(a_i+c\) \((1≤t≤g≤N,0≤c≤1000000000)\)。
操作3:“3 t g”(不含双引号)。询问所有满足\(t≤i≤g\)的\(a_i\)的和模P的值 \((1≤t≤g≤N)\)。
同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。

输出格式

对每个操作3,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。

输入输出样例

输入

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

输出

2
35
8

说明/提示

【样例说明】

初始时数列为(1,2,3,4,5,6,7)。

经过第1次操作后,数列为(1,10,15,20,25,6,7)。

对第2次操作,和为10+15+20=45,模43的结果是2。

经过第3次操作后,数列为(1,10,24,29,34,15,16}

对第4次操作,和为1+10+24=35,模43的结果是35。

对第5次操作,和为29+34+15+16=94,模43的结果是8。

测试数据规模如下表所示

数据编号 1 2 3 4 5 6 7 8 9 10

N= 10 1000 1000 10000 60000 70000 80000 90000 100000 100000

M= 10 1000 1000 10000 60000 70000 80000 90000 100000 100000

Source: Ahoi 2009

题目题解

还是三个操作,线段树基本题

//#define fre yes

#include <cstdio>
#define int long long

const int N = 100005;
struct Node {
    int l, r;
    long long sum, addv, mul;
} tree[N << 2];
int MOD;

long long ans;

void build(int k, int l, int r) {
    tree[k].l = l; tree[k].r = r; tree[k].addv = 0; tree[k].mul = 1;
    if(l == r) {
        scanf("%lld", &tree[k].sum);
        tree[k].sum %= MOD;
        return ;
    }

    int mid = (l + r) >> 1;
    build(k * 2, l, mid);
    build(k * 2 + 1, mid + 1, r);
    tree[k].sum = (tree[k * 2].sum + tree[k * 2 + 1].sum) % MOD;
}

void update(int k) {
    tree[k * 2].addv = (tree[k].mul * tree[k * 2].addv + tree[k].addv) % MOD;
    tree[k * 2 + 1].addv = (tree[k].mul * tree[k * 2 + 1].addv + tree[k].addv) % MOD;
    tree[k * 2].mul = (tree[k].mul * tree[k * 2].mul) % MOD;
    tree[k * 2 + 1].mul = (tree[k].mul * tree[k * 2 + 1].mul) % MOD;
    tree[k * 2].sum = (tree[k * 2].sum * tree[k].mul + (tree[k * 2].r - tree[k * 2].l + 1) * tree[k].addv) % MOD;
    tree[k * 2 + 1].sum = (tree[k * 2 + 1].sum * tree[k].mul + (tree[k * 2 + 1].r - tree[k * 2 + 1].l + 1) * tree[k].addv) % MOD;
    tree[k].addv = 0;
    tree[k].mul = 1;
}

void mul_interval(int k, int l, int r, int x) {
    if(tree[k].l >= l && tree[k].r <= r) {
        tree[k].sum = (tree[k].sum * x) % MOD;
        tree[k].addv = (tree[k].addv * x) % MOD;
        tree[k].mul = (tree[k].mul * x) % MOD;
        return ;
    }

    if(tree[k].addv || tree[k].mul != 1) update(k);
    int mid = (tree[k].l + tree[k].r) >> 1;
    if(mid >= l) mul_interval(k * 2, l, r, x);
    if(mid < r) mul_interval(k * 2 + 1, l, r, x);
    tree[k].sum = (tree[k * 2].sum + tree[k * 2 + 1].sum) % MOD;
}

void add_interval(int k, int l, int r, int x) {
    if(tree[k].l >= l && tree[k].r <= r) {
        tree[k].sum = (tree[k].sum + (tree[k].r - tree[k].l + 1) * x) % MOD;
        tree[k].addv = (tree[k].addv + x) % MOD;
        return ;
    }

    if(tree[k].addv || tree[k].mul != 1) update(k);
    int mid = (tree[k].l + tree[k].r) >> 1;
    if(mid >= l) add_interval(k * 2, l, r, x);
    if(mid < r) add_interval(k * 2 + 1, l, r, x);
    tree[k].sum = (tree[k * 2].sum + tree[k * 2 + 1].sum) % MOD;
}

void query(int k, int l, int r) {
    if(tree[k].l >= l && tree[k].r <= r) {
        ans = (ans + tree[k].sum) % MOD;
        return ;
    }

    if(tree[k].addv || tree[k].mul != 1) update(k);
    int mid = (tree[k].l + tree[k].r) >> 1;
    if(mid >= l) query(k * 2, l, r);
    if(mid < r) query(k * 2 + 1, l, r);
}

signed main() {
    static int n, m;
    scanf("%lld %lld", &n, &MOD);
    build(1, 1, n);
    scanf("%lld", &m);
    for (int i = 1; i <= m; i++) {
        int k;
        scanf("%lld", &k);
        if(k == 1) {
            int l, r, x;
            scanf("%lld %lld %lld", &l, &r, &x);
            mul_interval(1, l, r, x);
        }
        if(k == 2) {
            int l, r, x;
            scanf("%lld %lld %lld", &l, &r, &x);
            add_interval(1, l, r, x);
        }
        if(k == 3) {
            int l, r; ans = 0;
            scanf("%lld %lld", &l, &r);
            query(1, l, r);
            printf("%lld\n", ans % MOD);
        }
    } return 0;
}

原文地址:https://www.cnblogs.com/Nicoppa/p/11445895.html

时间: 2024-08-29 08:46:57

「线段树」[AHOI2009]维护序列的相关文章

「CF484E」Sign on Fence「整体二分」「线段树」

题意 给定一个长度为\(n\)的正整数序列,第\(i\)个数为\(h_i\),\(m\)个询问,每次询问\((l, r, w)\),为\([l, r]\)所有长度为\(w\)的子区间最小值的最大值.(类似于一类特殊的直方图最大子矩形问题) \(1 \leq n, m \leq 10^5\) 题解 我们考虑二分答案,这样\(n\)个数变成\(01\),若\(h_i\geq mid\)则为\(0\),否则为\(1\) 每次就相当于查询存不存在长度为\(w\)的连续\(1\).用线段树维护. 这有个问

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

洛谷 P2023 BZOJ 1798 [AHOI2009]维护序列

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