「BZOJ 1798」[Ahoi2009]维护序列seq

给定长度为 N N 的序列,执行以下有三种模 P 意义下的操作之一 M M 次:
1. 区间乘 c; 2.区间加 c c ;3.查询区间和。
1P109,1M,N105,0c109

直接线段树,维护两个标记,一个维护乘法,一个维护加法。但是区间乘一个数时,要把加法标记也乘上这个数。

#include <bits/stdc++.h>
int P, N, M, S[1 << 18], tag1[1 << 18], tag2[1 << 18], a[100010];
void Build(int u, int Ls, int Rs)
{
    tag1[u] = 1;
    if(Ls == Rs){ S[u] = a[Ls]; return ; }
    int Mid = Ls + Rs >> 1;
    Build(u << 1, Ls, Mid);
    Build(u << 1 | 1, Mid + 1, Rs);
    S[u] = (S[u << 1] + S[u << 1 | 1]) % P;
}
void Pd(int u, int Ls, int Rs)
{
    int Mid = Ls + Rs >> 1;
    if(tag1[u] != 1)
    {
        S[u << 1] = 1ll * S[u << 1] * tag1[u] % P;
        tag1[u << 1] = 1ll * tag1[u << 1] * tag1[u] % P;
        tag2[u << 1] = 1ll * tag2[u << 1] * tag1[u] % P;
        S[u << 1 | 1] = 1ll * S[u << 1 | 1] * tag1[u] % P;
        tag1[u << 1 | 1] = 1ll * tag1[u << 1 | 1] * tag1[u] % P;
        tag2[u << 1 | 1] = 1ll * tag2[u << 1 | 1] * tag1[u] % P;
        tag1[u] = 1;
    }
    if(tag2[u])
    {
        S[u << 1] = (S[u << 1] + 1ll * tag2[u] * (Mid - Ls + 1)) % P;
        tag2[u << 1] = (tag2[u << 1] + tag2[u]) % P;
        S[u << 1 | 1] = (S[u << 1 | 1] + 1ll * tag2[u] * (Rs - Mid)) % P;
        tag2[u << 1 | 1] = (tag2[u << 1 | 1] + tag2[u]) % P;
        tag2[u] = 0;
    }
}
void MD1(int u, int Ls, int Rs, int l, int r, int c)
{
    if(l <= Ls && Rs <= r)
    {
        S[u] = 1ll * S[u] * c % P;
        tag1[u] = 1ll * tag1[u] * c % P;
        tag2[u] = 1ll * tag2[u] * c % P;
        return ;
    }
    Pd(u, Ls, Rs);
    int Mid = Ls + Rs >> 1;
    if(l <= Mid) MD1(u << 1, Ls, Mid, l, r, c);
    if(r > Mid) MD1(u << 1 | 1, Mid + 1, Rs, l, r, c);
    S[u] = (S[u << 1] + S[u << 1 | 1]) % P;
}
void MD2(int u, int Ls, int Rs, int l, int r, int c)
{
    if(l <= Ls && Rs <= r)
    {
        S[u] = (S[u] + 1ll * (Rs - Ls + 1) * c) % P;
        tag2[u] = (tag2[u] + c) % P;
        return ;
    }
    Pd(u, Ls, Rs);
    int Mid = Ls + Rs >> 1;
    if(l <= Mid) MD2(u << 1, Ls, Mid, l, r, c);
    if(r > Mid) MD2(u << 1 | 1, Mid + 1, Rs, l, r, c);
    S[u] = (S[u << 1] + S[u << 1 | 1]) % P;
}
int QU(int u, int Ls, int Rs, int l, int r)
{
    if(l <= Ls && Rs <= r) return S[u];
    Pd(u, Ls, Rs);
    int Mid = Ls + Rs >> 1, t1 = 0, t2 = 0;
    if(l <= Mid) t1 = QU(u << 1, Ls, Mid, l, r);
    if(r > Mid) t2 = QU(u << 1 | 1, Mid + 1, Rs, l, r);
    S[u] = (S[u << 1] + S[u << 1 | 1]) % P;
    return (t1 + t2) % P; 
}
int main()
{
    scanf("%d %d", &N, &P);
    for(int i = 1; i <= N; ++i) scanf("%d", &a[i]);
    Build(1, 1, N);
    scanf("%d", &M);
    for(int i = 1; i <= M; ++i)
    {
        int opt, l, r, c;
        scanf("%d %d %d", &opt, &l, &r);
        if(opt == 1)
        {
            scanf("%d", &c);
            MD1(1, 1, N, l, r, c);
        }
        if(opt == 2)
        {
            scanf("%d", &c);
            MD2(1, 1, N, l, r, c);
        }
        if(opt == 3) printf("%d\n", QU(1, 1, N, l, r));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值