• bzoj1798 [Ahoi2009]Seq 维护序列seq


    1798: [Ahoi2009]Seq 维护序列seq

    Time Limit: 30 Sec  Memory Limit: 64 MB
    Submit: 5375  Solved: 1899
    [Submit][Status][Discuss]

    Description

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

    HINT

    【样例说明】

    初始时数列为(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

    Day1

    分析:很显然,本题要用线段树做,但是怎么用线段树倒是个问题.可以想到先建树,每次修改一个区间的值把这个区间所包含的区间的值给修改了即可,但是这样太花时间了,有没有省时间的方法呢?一个著名的思想lazy-tag就出现了,想要详细了解lazy-tag,可以取看看这篇博客:传送门.

           简单来说,如果要修改一个区间的值,如果这个区间刚好被完全包含了就直接返回,打上标记(要改成多少),那么下次要用子节点的时候将标记下传即可,显然,对于本题要打两个tag,那么sum[o] = sum[o] * mul[o] + add[o],sum为和,mul为乘的数,add为加的数,那么如果我们乘一个数c,sum[o] = sum[o] * mul[o] * c + add[o] * c,将mul[o] * c和add[o] * c看作两个整体,那么可以发现如果乘一个数c的话,add数组要*c,mul数组也要*c,同理,如果加一个数c,那么只需要add数组+c即可,因为在下传标记的时候既有加,又有减(可能为0),所以add和mul数组的计算一定要将这两种情况都计算到.

           如果在线段树上几个区间操作的性质相同的,先把要求的数用操作需要的量给表示出来,然后对于每一种操作看看需要维护的标记的变化,最后合并一下即可.

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    #define le l,mid,o * 2
    #define re mid + 1,r,o * 2 + 1
    
    using namespace std;
    
    const long long maxn = 1000010;
    
    long long n, p,a[maxn],add[maxn],mul[maxn],sum[maxn],m;
    
    void build(int l, int r, int o)
    {
        mul[o] = 1;
        add[o] = 0;
        if (l == r)
        {
            sum[o] = a[l];
            return;
        }
        int mid = (l + r) >> 1;
        build(le);
        build(re);
        sum[o] = (sum[o * 2] + sum[o * 2 + 1]) % p;
    }
    
    void pushdown(int o, int k)
    {
        sum[o * 2] = (sum[o * 2] * mul[o] + add[o] * (k - (k >> 1))) % p;
        sum[o * 2 + 1] = (sum[o * 2 + 1] * mul[o] + add[o] * (k >> 1)) % p;
        mul[o * 2] = mul[o * 2] * mul[o] % p;
        mul[o * 2 + 1] = mul[o * 2 + 1] * mul[o] % p;
        add[o * 2] = (add[o * 2] * mul[o] + add[o]) % p;
        add[o * 2 + 1] = (add[o * 2 + 1] * mul[o] + add[o]) % p;
        mul[o] = 1;
        add[o] = 0;
    }
    
    void jia(int l, int r, int o, int x, int y, int v)
    {
        if (x <= l && r <= y)
        {
            add[o] = (add[o] + v) % p;
            sum[o] = (sum[o] + v * (r - l + 1)) % p;
            return;
        }
        pushdown(o, r - l + 1);
        int mid = (l + r) >> 1;
        if (x <= mid)
            jia(le, x, y, v);
        if (y > mid)
            jia(re, x, y, v);
        sum[o] = (sum[o * 2] + sum[o * 2 + 1]) % p;
    }
    
    void cheng(int l, int r, int o, int x, int y, int v)
    {
        if (x <= l && r <= y)
        {
            add[o] = (add[o] * v) % p;
            mul[o] = (mul[o] * v) % p;
            sum[o] = (sum[o] * v) % p;
            return;
        }
        pushdown(o, r - l + 1);
        int mid = (l + r) >> 1;
        if (x <= mid)
            cheng(le, x, y, v);
        if (y > mid)
            cheng(re, x, y, v);
        sum[o] = (sum[o * 2] + sum[o * 2 + 1]) % p;
    }
    
    long long query(int l, int r, int o, int x, int y)
    {
        if (x <= l && r <= y)
            return sum[o] % p;
        int mid = (l + r) >> 1;
        pushdown(o, r - l + 1);
        long long temp = 0;
        if (x <= mid)
            temp = (temp + query(le, x, y)) % p;
        if (y > mid)
            temp = (temp + query(re, x, y)) % p;
        sum[o] = (sum[o * 2] + sum[o * 2 + 1]) % p;
        return temp % p;
    }
    
    int main()
    {
        scanf("%lld%lld", &n, &p);
        for (int i = 1; i <= n; i++)
            scanf("%lld", &a[i]);
        build(1, n, 1);
        scanf("%lld", &m);
        while (m--)
        {
            int op, t, g,c;
            scanf("%d%d%d", &op, &t, &g);
            if (op == 1)
            {
                scanf("%d", &c);
                cheng(1, n, 1, t, g, c);
            }
            if (op == 2)
            {
                scanf("%d", &c);
                jia(1, n, 1, t, g, c);
            }
            if (op == 3)
                printf("%lld
    ", query(1, n, 1, t, g) % p);
        }
    
        return 0;
    }
  • 相关阅读:
    在eclipse中使用maven构建spring cloud微服务
    SpringBoot中VO,DTO,DO,PO的概念、区别和用处
    报错Connection refused: connect
    @RequestBody的使用
    Chrome插件Postman的数据目录存储位置,记一次重装系统后找回postman数据的过程...
    SpringBoot中VO,DTO,DO,PO的概念、区别和用处
    Oracle中的instr()函数 详解及应用
    for循环里的break,continue和return有什么差别
    BigDecimal转String
    字符串转为日期,日期转为字符串
  • 原文地址:https://www.cnblogs.com/zbtrs/p/5859819.html
Copyright © 2020-2023  润新知