• P3373 【模板】线段树 2(板子好题)


    题目描述

    如题,已知一个数列,你需要进行下面三种操作:

    • 将某区间每一个数乘上 xxx
    • 将某区间每一个数加上 xxx
    • 求出某区间每一个数的和

    输入格式

    第一行包含三个整数 n,m,pn,m,pn,m,p,分别表示该数列数字的个数、操作的总个数和模数。

    第二行包含 nnn 个用空格分隔的整数,其中第 iii 个数字表示数列第 iii 项的初始值。

    接下来 mmm 行每行包含若干个整数,表示一个操作,具体如下:

    操作 111: 格式:1 x y k 含义:将区间 [x,y][x,y][x,y] 内每个数乘上 kkk

    操作 222: 格式:2 x y k 含义:将区间 [x,y][x,y][x,y] 内每个数加上 kkk

    操作 333: 格式:3 x y 含义:输出区间 [x,y][x,y][x,y] 内每个数的和对 ppp 取模所得的结果

    输出格式

    输出包含若干行整数,即为所有操作 333 的结果。

    输入输出样例

    输入 #1 复制

    5 5 38

    1 5 4 2 3

    2 1 4 1

    3 2 5

    1 2 4 2

    2 3 5 5

    3 1 4

    输出 #1 复制

    17

    2

    由于需要同时进行区间乘和区间加操作,显然需要两个lazy tag,而关键就是确定加法和乘法的优先级。这里举一个例子:

    一、如果是先加后乘的话,对于某一个节点a[i]:

    1)+2:  sum = a[i] + 2

    2) *3:  sum = (a[i] + 2) * 3

    这时一切都很正常,但如果接下来再执行+2操作,期望的结果是sum = (a[i] + 2) * 3 + 2,然而得到的却是sum = (a[i] + 2 + 2) * 3, 因为lazy tag的效果是累加的。因此需要使+ 2变成+ 2 / 3, 扩展到了实数域会带来精度损失,这就是先加后乘的弊端。

    一、如果是先乘后加的话,对于某一个节点a[i]:

    1)+2:  sum = a[i] + 2

    2) *3:  sum = a[i] * 3 + 2 * 3

    3)+2:  sum = a[i] * 3 + 2 * 3 + 2

    4) *4:  sum = a[i] * 3 * 4 + 2 * 3 * 4 + 2 * 4

    看起来很完美,因此选择先乘后加。这样需要注意一下change函数和spread函数。change的时候,如果是进行区间加法则正常进行,如果是进行区间乘法,则记得给加法标记也乘一下k。对于spread函数,要按照:更新sum值-更新乘法标记-更新加法标记-父节点清除加法标记乘法标记的顺序写。

    需要注意的地方:建树时初始化乘法标记为1,清除标记时乘法标记清除为1.

    不要多加冗余的取模运算,会T

    #include <bits/stdc++.h>
    #define SIZE 100005
    #define int long long
    using namespace std;
    int a[SIZE], n, q, mod;
    inline int read(){
        int a = 0; int f = 0;char p = getchar();
        while(!isdigit(p)){f |= p == '-'; p = getchar();}
        while(isdigit(p)){a = (a << 3) + (a << 1) + (p ^ 48); p = getchar();}
        return f ? -a : a;
    }
    struct SegmentTree
    {
        int p;
        int l;
        int r;
        long long sum;
        long long add;
        long long mult;
    } t[4*SIZE];
    void build(int p, int l, int r)
    {
        t[p].l = l, t[p].r = r, t[p].mult = 1;//注意初始化mult 
        if(l == r)    {    t[p].sum = 1ll * a[l] % mod; return;    }//注意build函数 
        int mid = (l + r) >> 1;
        build(2 * p, l, mid);
        build(2 * p + 1, mid + 1, r);
        t[p].sum = 1ll * (t[2 * p].sum + t[2 * p + 1].sum) % mod;
    }
    void spread(int p)
    {
        t[2 * p].sum = 1ll * (t[p].mult * t[2 * p].sum + t[p].add * (t[2 * p].r - t[2 * p].l + 1) % mod) % mod;//此处父节点的加法标记已经乘过了,因此也满足先乘后加 
        t[2 * p + 1].sum = 1ll * (t[p].mult * t[2 * p + 1].sum % mod + t[p].add * (t[2 * p + 1].r - t[2 * p + 1].l + 1) % mod) % mod;
        t[2 * p].mult = 1ll * t[2 * p].mult * t[p].mult % mod;
        t[2 * p + 1].mult = 1ll * t[2 * p + 1].mult * t[p].mult % mod;
        t[2 * p].add = 1ll * (t[p].add + t[2 * p].add * t[p].mult) % mod;
        t[2 * p + 1].add = 1ll * (t[p].add + t[2 * p + 1].add * t[p].mult % mod) % mod;
        t[p].add = 0;
        t[p].mult = 1;
    }
    void change(int op, int p, int l, int r, int k)
    {
        if(t[p].l >= l && t[p].r <= r)//完全覆盖 
        {
            if(op == 1)
            {
                t[p].add = 1ll * t[p].add * k % mod;
                t[p].sum = 1ll * (t[p].sum * k) % mod;
                t[p].mult = 1ll * t[p].mult * k % mod;
                return;
            }
            else
            {
                t[p].sum = 1ll * (t[p].sum + (long long)k * (t[p].r - t[p].l + 1) ) % mod;
                t[p].add = 1ll * (t[p].add + k) % mod;
                return;
            }
        }
        spread(p);
        t[p].sum = 1ll * (t[2 * p].sum  + t[2 * p + 1].sum ) % mod;
        int mid = (t[p].l + t[p].r) >> 1;
        if(l <= mid) change(op, 2 * p, l, r, k);
        if(r > mid) change(op, 2 * p + 1, l, r, k);
        t[p].sum = 1ll * (t[2 * p].sum + t[2 * p + 1].sum) % mod;
    }
    long long ask(int p, int l, int r)
    {
        if(t[p].l >= l && t[p].r <= r) return t[p].sum;
        spread(p);
        int mid = (t[p].l + t[p].r) >> 1;
        long long val = 0;
        if(l <= mid) val = 1ll * (val + ask(2 * p, l, r)) % mod;
        if(r > mid) val = 1ll * (val + ask(2 * p + 1, l, r)) % mod;
        return val;
    }
    signed main()
    {
        cin >> n >> q >> mod;
        int i, op, x, y, k;
        for(i = 1; i <= n; i++) a[i] = read();
        build(1, 1, n);
        for(i = 1; i <= q; i++)
        {
            op = read(), x = read(), y = read();
            if(op == 1)
            {
                k = read();
                change(1, 1, x, y, k);
            }
            else if(op == 2)
            {
                k = read();
                change(2, 1, x, y, k);
            }
            else cout << ask(1, x, y) << endl;
        }
        return 0;
    }
  • 相关阅读:
    OSX安装nginx和rtmp模块(rtmp直播服务器搭建)
    用runtime来重写Coder和deCode方法 归档解档的时候使用
    Homebrew安装卸载
    Cannot create a new pixel buffer adaptor with an asset writer input that has already started writing'
    OSX下面用ffmpeg抓取桌面以及摄像头推流进行直播
    让nginx支持HLS
    iOS 字典转json字符串
    iOS 七牛多张图片上传
    iOS9UICollectionView自定义布局modifying attributes returned by UICollectionViewFlowLayout without copying them
    Xcode6 iOS7模拟器和Xcode7 iOS8模拟器离线下载
  • 原文地址:https://www.cnblogs.com/lipoicyclic/p/13265829.html
Copyright © 2020-2023  润新知