码迷,mamicode.com
首页 > 其他好文 > 详细

线段树—区间乘法

时间:2020-06-28 22:27:48      阅读:78      评论:0      收藏:0      [点我收藏+]

标签:很多   date   输出   std   大致   输入格式   build   update   运算   

线段树区间乘法

题目描述

如题,已知一个数列,你需要进行下面三种操作:
将某区间每一个数乘上\(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.千万不要直接推到底,那样时间效率太低了,本人没有试过会不会超时,有效率高的的代码何必要打效率第的呢?

线段树—区间乘法

标签:很多   date   输出   std   大致   输入格式   build   update   运算   

原文地址:https://www.cnblogs.com/hzoi-liujiahui/p/13205040.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!