bzoj1798题解

  • Description
    老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成。 有长为N(1≤N≤100000)的数列,不妨设为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

  • Solution
    线段树上打两个标记还是头一次写,一开始忽略了两个标记的联系,就分别维护了加乘标记,结果WA不止。实际上标记间的关系还是很明确的。(详见代码pushdown函数)又加又乘的弄得zkw不好用,用了个堆写法。

#include<cstdio>
#include<algorithm>
#define LL unsigned long long

using namespace std;

const int maxn = 300000;

struct tree {
    LL data, lc, rc, lazy, mul;
    tree(LL data=0, LL lc=0, LL rc=0, LL lazy=0, LL mul=0) :
        data(data), lc(lc), rc(rc), lazy(lazy), mul(mul) {}
}T[maxn];
//当前区间和,左儿子,右儿子,加标记,乘标记

int n, m, p;
LL num[100001], root = 1;
//-----------------读写优化--------------------
inline void read(LL &a) {
    int f = 1;
    char ch = getchar();
    a = 0;
    while(ch < '0' || ch > '9') {
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9') {
        a = a*10 + ch - 48;
        ch = getchar();
    }
    a *= f;
}
inline void read(int &a) {
    int f = 1;
    char ch = getchar();
    a = 0;
    while(ch < '0' || ch > '9') {
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9') {
        a = a*10 + ch - 48;
        ch = getchar();
    }
    a *= f;
}
inline void write(LL a) {
    int top = 0;
    char ch[40];
    if(a < 0) {
        putchar('-');
        a = -a;
    }
    do {
        ch[top++] = a%10 + 48;
        a /= 10;
    } while(a);
    while(top--) putchar(ch[top]);
    putchar('\n');
}
//-------------------------------------
//--------------线段树------------------
//根,根所对应的线段长度
inline void pushdown(int rot, int d) { 
    T[rot<<1].data = (T[rot<<1].data*T[rot].mul + (d-(d>>1))*T[rot].lazy)%p;
    T[rot<<1|1].data = (T[rot<<1|1].data*T[rot].mul + (d>>1)*T[rot].lazy)%p;

    T[rot<<1].mul = T[rot<<1].mul*T[rot].mul%p;
    T[rot<<1|1].mul = T[rot<<1|1].mul*T[rot].mul%p;

    T[rot<<1].lazy = (T[rot<<1].lazy*T[rot].mul + T[rot].lazy)%p;
    T[rot<<1|1].lazy = (T[rot<<1|1].lazy*T[rot].mul + T[rot].lazy)%p;

    T[rot].mul = 1; T[rot].lazy = 0;
}

//根,区间左端点,区间右端点
void build(int rot, int x, int y) {
    if(x + 1 <= y) {
        build(rot<<1, x, (x+y)>>1);
        build(rot<<1|1, ((x+y)>>1)+1, y);
        T[rot] = tree((T[rot<<1].data + T[rot<<1|1].data)%p, x, y, 0, 1);
    }
    else T[rot] = tree(num[x], x, x, 0, 1); 
}

//根,区间左端点,区间右端点,加标记,乘标记
void change(int rot, int x, int y, LL d, LL m) {
    if(x <= T[rot].lc && y >= T[rot].rc) {
    T[rot].data=(T[rot].data*m+(T[rot].rc-T[rot].lc+1)*d)%p;
        T[rot].mul = T[rot].mul*m%p;
        T[rot].lazy = (T[rot].lazy*m + d)%p;
        return;
    }
    pushdown(rot, T[rot].rc-T[rot].lc+1);
    if(x <= ((T[rot].lc+T[rot].rc)>>1)) 
        change(rot<<1, x, y, d, m);
    if(y > ((T[rot].lc+T[rot].rc)>>1)) 
        change(rot<<1|1, x, y, d, m);
    T[rot].data = (T[rot<<1].data+T[rot<<1|1].data)%p;
}

//根,区间左端点,区间右端点
LL query(int rot, int x, int y) {
    if(x <= T[rot].lc && y >= T[rot].rc) 
        return T[rot].data%p;
    pushdown(rot, T[rot].rc-T[rot].lc+1);
    LL ans = 0;
    if(x <= ((T[rot].lc+T[rot].rc)>>1)) 
        ans = (ans+query(rot<<1, x, y))%p;
    if(y > ((T[rot].lc+T[rot].rc)>>1)) 
        ans = (ans+query(rot<<1|1, x, y))%p;
    return ans%p;
}

int main() {
    int opt, c, l, r;
    read(n); read(p);
    for(int i = 1; i <= n; ++i) 
        { read(num[i]); num[i] %= p; }
    build(root, 1, n);
    read(m);
    for(int i = 1; i <= m; ++i) {
        read(opt); read(l); read(r);
        switch(opt) {
            case 1 :read(c); change(root, l, r, 0, c); 
                    break;
            case 2 :read(c); change(root, l, r, c, 1); 
                    break;
            case 3 :write(query(root, l, r)); 
                    break;
            default:;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值