线段树—区间乘法

线段树区间乘法

题目描述

如题,已知一个数列,你需要进行下面三种操作:
将某区间每一个数乘上\(x\)
将某区间每一个数加上\(x\)
求出某区间每一个数的和

输入格式

第一行包含三个整数\(n,m,p\),分别表示该数列数字的个数、操作的总个数和模数。
第二行包含\(n\)个用空格分隔的整数,其中第\(i\)个数字表示数列第\(i\)项的初始值。
接下来\(m\)行每行包含若干个整数,表示一个操作,具体如下:
操作\(1\): 格式:\(1 x y k\) 含义:将区间\([x,y]\)内每个数乘上\(k\)
操作\(2\): 格式:\(2 x y k\) 含义:将区间\([x,y]\)内每个数加上\(k\)
操作\(3\): 格式:\(3 x y\) 含义:输出区间\([x,y]\)内每个数的和对\(p\)取模所得的结果

输出格式

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

输入输出样例

输入

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

输出

17
2

说明/提示

【数据范围】
对于\(30%\)的数据:\(n≤8\)\(m≤10\)
对于\(70%\)的数据:\(n≤10^3\)\(m≤10^4\)
对于\(100%\)的数据:\(n≤10^5\),\(m≤10^5\)
除样例外,p=571373

大致思路

这道题和普通的线段树的不同之处就在于可以用乘法,那么思考一个问题,用乘法就考虑到了四则运算的顺序问题,要是lazy标志不及时的推下去,就可能导致乘法在加法还没有运算的时候乘上去,显然答案就是错误的了,那么就体应该如何操作呢?
这样想,要是将乘法和加法用两个lazy标志分开计算的话,好像并不好操作,那简化一下,运用乘法分配率即\((a+lazy[b])*lazy_[c]=a*lazy_[c]+lazy[b]*lazy_[c]\),说白了就是先乘后加,再向下推lazy标记的时候就方便了很多。

代码实现

#include<bits/stdc++.h>
const int maxn = 1e5+5;

long long tree[maxn << 2],lazy[maxn << 2], lazy_[maxn << 2],x[maxn];//线段树不要忘记要左移两位,数组别开小了
int n,m,order,add,mod;
using namespace std;

void build(int rt, int l, int r){
    lazy_[rt] = 1;
    if(l == r){
        tree[rt] = x[l] % mod;
        return;
    }
    int mid = (l + r) >> 1;
    build(rt << 1, l, mid);
    build(rt << 1 | 1, mid + 1, r);
    tree[rt] = (tree[rt << 1] + tree[rt << 1 | 1])%mod;
}

void update(int rt, int l, int r, long long w, long long cheng){
    lazy_[rt] =(lazy_[rt]*cheng)%mod;
    lazy[rt] = (lazy[rt]*cheng) %mod;//先乘
    lazy[rt] = (lazy[rt]+ w)%mod;//后加
    tree[rt] =( tree[rt] * cheng + (r - l + 1)*w )%mod;
}

void pushdown(int rt, int l, int r){
    int mid = (l + r) >> 1;
    update(rt << 1, l, mid, lazy[rt],lazy_[rt]);
    update(rt << 1 | 1, mid + 1, r, lazy[rt], lazy_[rt]);
    lazy[rt] = 0;
    lazy_[rt] = 1;
}

void modify(int rt, int l, int r, int s, int t, long long w, long long cheng){//通俗易懂,w就是加,cheng就是cheng,此函数就是区间修改
    if(l >= s && r <= t){
        update(rt, l, r, w, cheng);//直接修改
        return;
    }
    pushdown(rt,l,r);//lazy标记下推
    int mid = (l + r) >> 1;
    if(mid >= s) modify(rt << 1, l, mid, s, t, w, cheng);
    if(mid < t) modify(rt << 1 | 1, mid + 1, r, s, t, w, cheng);
    tree[rt] = (tree[rt << 1] + tree[rt << 1 | 1])%mod;
}

long long query(int rt, int l, int r, int s, int t){//区间查询和
    if(l >= s && r <= t){
        return tree[rt]%mod;
    }
    int mid = (l + r) >> 1;
    pushdown(rt, l, r);
    long long ans = 0;
    if(mid >= s) ans = (ans+query(rt << 1, l, mid, s, t)) % mod;
    if(mid < t) ans = (ans + query(rt << 1 | 1, mid + 1, r, s, t)) % mod;
    return ans%mod;
}

void solve(){
    int a,b,add;
    scanf("%d%d%d", &n, &m, &mod);
    for(int i = 1; i <= n; i++) scanf("%lld",&x[i]);
    build(1,1,n);
    for(int i = 1; i <= m; i++){
        scanf("%d",&order);
        if(order == 2){
            scanf("%d%d%d",&a, &b, &add);
            modify(1, 1, n, a, b, add,1);
        }else if(order == 1){
            scanf("%d%d%d", &a, &b, &add);
            modify(1, 1, n, a, b, 0, add);
        }
        else{
            scanf("%d%d", &a, &b);
            printf("%lld\n",query(1,1,n,a,b)%mod);
        }
    }
}
int main(){
    solve();
    return 0;
}

提示:

1.mod一定要及时取,该取mod就取,不要少,我就是调这个mod卡了将近两个小时,也不要取太多,同学有tle的。
2.long long要记得开上,否则就是wa。
3.千万不要直接推到底,那样时间效率太低了,本人没有试过会不会超时,有效率高的的代码何必要打效率低的呢?

原文链接: https://www.cnblogs.com/hzoi-liujiahui/p/13205040.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    线段树—区间乘法

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/360118

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月2日 下午1:06
下一篇 2023年3月2日 下午1:07

相关推荐