zoukankan      html  css  js  c++  java
  • 线段树-模板

    透彻线段树


    1.

    区间加

    #include <bits/stdc++.h>
    using namespace std;
    /*
    如果人可以长尾巴
    
    会觉得有点难为情呢
    
    因为只要和你在一起,我总会忍不住摇尾巴吧
    */
    const int maxn = 1e6 + 10;
    
    #define ls(now) (now << 1)
    #define rs(now) (now<<1|1)
    #define mid ((l + r) >> 1)
    
    int n, m, a[maxn];
    
    struct seg_tree{
    	struct nodes{
    		long long l, r, sum, tag;
    		long long get(){
    			return sum + (r - l + 1) * tag;
    		}
    	}node[maxn];
    	void up(int now){
    		return (void)(node[now].sum = node[ls(now)].get() + node[rs(now)].get());
    	}
    	void down(int now){
    		return (void)(node[ls(now)].tag += node[now].tag, node[rs(now)].tag += node[now].tag, node[now].tag = 0);
    	}
    	void bulid(int l, int r, int now){
    		node[now].l = l, node[now].r = r;
    		if(l == r) return (void)(node[now].sum = a[l]);
    		bulid(l, mid, ls(now)), bulid(mid+1, r, rs(now));
    		up(now);
    	}
    	void chenge(int l, int r, int now, int val){
    		if(node[now].r < l or node[now].l > r) return;
    		if(l <= node[now].l and node[now].r <= r) return (void)(node[now].tag += val);
    		down(now);
    		chenge(l, r, ls(now), val), chenge(l, r, rs(now), val);
    		up(now);
    	}
    	void query(int l, int r, int now, long long &ans){
    		if(l > node[now].r or r < node[now].l) return;
    		if(l <= node[now].l and node[now].r <= r) return (void)(ans += node[now].get());
    		down(now);
    		query(l, r, ls(now), ans), query(l, r, rs(now), ans);
    		up(now);
    	}
    
    }tree;
    
    signed main(){
    	scanf("%d%d", &n, &m);
    	for(int i = 1; i <= n; i ++){
    		scanf("%d", &a[i]);
    	}
    	tree.bulid(1, n, 1);
    	for(int cmp, x, y, z; m ; m --){
    		scanf("%d", &cmp);
    		if(cmp == 1){
    			scanf("%d%d%d", &x, &y, &z);
    			tree.chenge(x, y, 1, z);
    		}
    		if(cmp == 2){
    			long long ans = 0;
    			scanf("%d%d", &x, &y);
    			tree.query(x, y, 1, ans);
    			printf("%lld
    ", ans);
    		}
    	}
    	return 0;
    }
    

    2.

    区间乘+区间加

    加法和乘法顺序不一样会导致不同的结果

    比如: ((a+b)*c) 不等于 (ac+b)

    而在记录懒标记的时候,加法和乘法两种标记放到一起,并不知道哪个先,哪个后。

    所以要确定一个优先级

    我们分析一下两种顺序:

    1. 先加后乘 : ((a+b)*c) = (ac + b*c)

    2. 先乘后加:(a*c+b)

    比较一下,发现,上面的先加后乘相当于下面的式子,在加法上面多乘了一个(c)

    所以,我们只要是先加后乘的式子,只要加一个(*c)就可以转化为先乘后加的式子

    具体的操作就是在添加乘法标记的时候,把加法标记(*c)就好了

    所以,我们就定了一个总顺序:先乘后加(摘自luogu博客)

    然后在down下放标记的时候,左右儿子的加法标记传递也要保持先乘后加的顺序,即 :

    void down(int now){
    	t[ls(now)].tmp = (t[ls(now)].tmp*t[now].tmp)%p;
    	t[rs(now)].tmp = (t[rs(now)].tmp*t[now].tmp)%p; 
    	t[ls(now)].tag = (t[ls(now)].tag*t[now].tmp)%p;
    	t[rs(now)].tag = (t[rs(now)].tag*t[now].tmp)%p; 
    	t[ls(now)].tag = (t[ls(now)].tag+t[now].tag)%p;
    	t[rs(now)].tag = (t[rs(now)].tag+t[now].tag)%p;
    	t[now].tag = 0, t[now].tmp = 1;
    	return;
    }
    

    解决方法:先乘后加

    //对于每一次的区间乘val,我们对在此次操作前做的区间加也乘上这次的val
    t[now].tmp *= val, t[now].tag *= val
    

    完美的解决了问题

    #include <bits/stdc++.h>
    using namespace std;
    /*
    如果人可以长尾巴
    
    会觉得有点难为情呢
    
    因为只要和你在一起,我总会忍不住摇尾巴吧
    */
    const int maxn = 1e6 + 10;
    
    #define ls(now) (now << 1)
    #define rs(noe) (now<<1|1)
    #define mid ((l + r) >> 1)
    
    int n, m, p, a[maxn];
    long long ans;
    
    struct seg_tree{
    
    	struct nodes{
    		long long l, r, sum, tag, tmp;
    		nodes(){
    			tmp = 1;
    			tag = 0;
    		}
    		long long get(){
    			return (((sum%p)*tmp%p)%p + ((r-l+1)*tag)%p);
    		}
    	}t[maxn];
    
    	void up(int now){
    		return (void)(t[now].sum = t[ls(now)].get() + t[rs(now)].get());
    	}
    	void down(int now){
    		t[ls(now)].tmp = (t[ls(now)].tmp*t[now].tmp)%p;
    		t[rs(now)].tmp = (t[rs(now)].tmp*t[now].tmp)%p; 
    		t[ls(now)].tag = (t[ls(now)].tag*t[now].tmp)%p;
    		t[rs(now)].tag = (t[rs(now)].tag*t[now].tmp)%p; 
    		t[ls(now)].tag = (t[ls(now)].tag+t[now].tag)%p;
    		t[rs(now)].tag = (t[rs(now)].tag+t[now].tag)%p;
    		t[now].tag = 0, t[now].tmp = 1;
    		return;
    	}
    	void bulid(int l, int r, int now){
    		t[now].l = l, t[now].r = r;
    		if(l == r) return (void)(t[now].sum = a[l]);
    		bulid(l, mid, ls(now)), bulid(mid+1, r, rs(now));
    		up(now);
    	}
    	void chenge(int l, int r, int now, int val){
    		if(r < t[now].l or l > t[now].r) return;
    		if(l <= t[now].l and t[now].r <= r) return (void)(t[now].tag += val);
    		down(now);
    		chenge(l, r, ls(now), val), chenge(l, r, rs(now), val);
    		up(now);
    	}
    	void change(int l, int r, int now, int val){
    		if(r < t[now].l or l > t[now].r) return;
    		if(l <= t[now].l and t[now].r <= r) return (void)(t[now].tmp *= val, t[now].tag *= val);
    		down(now);
    		change(l, r, ls(now), val), change(l, r, rs(now), val);
    		up(now);
    	}
    	void query(int l, int r, int now, long long &ans){
    		if(r < t[now].l or l > t[now].r) return;
    		if(l <= t[now].l and t[now].r <= r) return (void)(ans += t[now].get(), ans %= p);
    		down(now);
    		query(l, r, ls(now), ans), query(l, r, rs(now), ans);
    		up(now);
    	}
    }tree;
    
    signed main(){
    	scanf("%d%d%d", &n, &m, &p);
    	for(int i = 1; i <= n; i ++){
    		scanf("%d", &a[i]);
    	}
    	tree.bulid(1, n, 1);
    	for(int cmp, x, y, z; m; m --){
    		scanf("%d", &cmp);
    		if(cmp == 1){
    			scanf("%d%d%d", &x, &y, &z);
    			tree.change(x, y, 1, z);
    		}
    		if(cmp == 2){
    			scanf("%d%d%d", &x, &y, &z);
    			tree.chenge(x, y, 1, z);
    		}
    		if(cmp == 3){
    			scanf("%d%d", &x, &y);
    			tree.query(x, y, 1, ans);
    			printf("%lld
    ", ans);
    			ans = 0;
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    【2021-05-18】人生十三信条
    【2021-05-17】打了第一针疫苗
    【2021-05-16】该工作时好好工作,该休息时好好休息
    【2021-05-15】人生十三信条
    【2021-05-14】要保持团队作战的模式
    【2021-05-13】罗马不是一天能建成的
    【2021-05-12】己所不欲勿施于人
    【2021-05-11】服务好了别人,也就服务好了自己
    二维区域和检索
    寻找重复数
  • 原文地址:https://www.cnblogs.com/Vanyun/p/13283391.html
Copyright © 2011-2022 走看看