zoukankan      html  css  js  c++  java
  • 线段树—区间乘法

    线段树区间乘法

    题目描述

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

  • 相关阅读:
    jacman主题分页出现问题(Next<span></span>)
    后会有期(非技术)
    再谈前端性能优化
    emmet常用指令组合
    imagemagick在windows下安装(转,有改动)
    nth-child和蝉原则实现的奇妙随机效果(译)
    flex布局浅谈和实例
    css命名那些事儿
    chrome开发者工具浅析--timeline
    浏览器历史和发展趋势浅析
  • 原文地址:https://www.cnblogs.com/hzoi-liujiahui/p/13205040.html
Copyright © 2011-2022 走看看