「线段树」[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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值