线段树—区间乘法
Posted hzoi-liujiahui
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树—区间乘法相关的知识,希望对你有一定的参考价值。
线段树区间乘法
题目描述
如题,已知一个数列,你需要进行下面三种操作:
将某区间每一个数乘上(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
",query(1,1,n,a,b)%mod);
}
}
}
int main(){
solve();
return 0;
}
提示:
1.mod一定要及时取,该取mod就取,不要少,我就是调这个mod卡了将近两个小时,也不要取太多,同学有tle的。
2.long long要记得开上,否则就是wa。
3.千万不要直接推到底,那样时间效率太低了,本人没有试过会不会超时,有效率高的的代码何必要打效率第的呢?
以上是关于线段树—区间乘法的主要内容,如果未能解决你的问题,请参考以下文章
P2023 [AHOI2009]维护序列 - 线段树区间乘法加法