线段树—区间乘法

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]维护序列 - 线段树区间乘法加法

加法乘法线段树模板

进阶线段树之乘法操作

线段树的复杂操作

Codeforces 719E [斐波那契区间操作][矩阵快速幂][线段树区间更新]

P3373 线段树乘法模板 P2023 [AHOI2009]维护序列