• 线段树模板二[半转载..


    转载声明

    解释来自:https://www.luogu.org/blog/running-coder/solution-p3373
    进入正题:(以下内容均在sgt结构体内)

    此题相对于模板一,加了个区间乘,于是在模板一的基础上需要多开个数组(记录乘法懒标记)、多写个函数(区间乘),还有要把懒标记下放函数做些修改。

    变量定义:
    sum[]:线段树节点对应区间的元素总和;

    addv[]:线段树节点对应区间的所有元素待加的值(懒标记),初值全部设为0;

    mulv[]:线段树节点对应区间的所有元素待乘的值(懒标记),初值全部设为1。

    过程说明:
    建树(Build):
    同模板一。。。

    懒标记下放(Push_down):
    原理解释:

    1.当对某区间执行加法操作时,由于加法优先级低,不会对乘法操作产生影响,故直接相加即可;

    2.当对某区间执行乘法操作时,由于乘法优先级高,会对之前的加法操作产生影响,故需要在相乘时不仅对sum和mulv相乘,也需要对addv相乘;

    3.由于上述原因,故需要先算乘法再算加法。

    细节实现:

    1.子树的sum、mulv、addv值分别乘上当前节点的mulv值;

    2.当前节点的mulv值还原,即置为1;

    3.子树的addv值加上当前节点的addv值;

    4.子树的sum值加上(子树包含元素数量*当前节点的addv值);

    5.当前节点的addv值还原,即置为0。

    特别说明:

    1.使用前判断,若当前节点的懒标记为空则不需执行此下放函数。虽然执行了也不会有影响,但浪费时间;

    2.为尽量节省时间,要将判断放在此函数外而不是函数内。

    区间加(Addall):
    同模板一。。。

    区间乘(Mulall):
    若当前节点完全包含在待更新区间内,则直接修改当前节点的mulv、addv、sum值即可(参考下放函数说明);

    否则执行与区间加类似的操作即可。

    区间查询(Query):
    同模板一。。。

    提示:不要忘记取模。。。

    个人代码

    luoguP3373

    #include<cstdio>
    using namespace std;
    #define MAX 100000+99
    #define ll long long
    
    int n,m,p;
    ll sumv[MAX<<2], addv[MAX<<2], mulv[MAX<<2];
    ll a[MAX];
    
    void push_up(int o) { sumv[o] = (sumv[o<<1] + sumv[o<<1|1] ) % p;}
    void build(int o, int l, int r) {
    	addv[o] = 0; mulv[o] = 1;
    	if(l == r) { sumv[o] = a[l]; return;}
    	int mid = (l+r)>>1;
    	build(o<<1, l, mid);
    	build(o<<1|1, mid+1, r);
    	push_up(o);
    }
    
    //void pushtag()这里的pushtag不一样,我就不写了
    void push_down(int o, int l, int r) {
    	if(mulv[o] != 1) {//先传mul 
    		mulv[o<<1] = (mulv[o<<1] * mulv[o]) % p;
    		mulv[o<<1|1] = (mulv[o<<1|1] * mulv[o]) % p;
    		addv[o<<1] = (addv[o<<1] * mulv[o]) % p;
    		addv[o<<1|1] = (addv[o<<1|1] * mulv[o]) % p;
    		sumv[o<<1] = (sumv[o<<1] * mulv[o]) % p;
    		sumv[o<<1|1] = (sumv[o<<1|1] * mulv[o]) % p;
    		mulv[o] = 1;
    	}
    	if(addv[o]) {
    		addv[o<<1] = (addv[o<<1] + addv[o]) % p;
    		addv[o<<1|1] = (addv[o<<1|1] + addv[o]) % p;
    		int mid = (l + r) >> 1;
    		sumv[o<<1] = (sumv[o<<1] + addv[o]*(mid-l+1) ) % p;
    		sumv[o<<1|1] = (sumv[o<<1|1] + addv[o]*(r-mid) ) % p ;
    		addv[o] = 0;
    	}
    } 
    
    void optadd(int o, int l, int r, int ql, int qr, int v) {
    	if(ql <= l && r <= qr) {
    		addv[o] = (ll)(addv[o] + v) % p;
    		sumv[o] = (ll)(sumv[o] + v*(r-l+1) ) % p;
    		return ;
    	}
    	push_down(o,l,r);
    	int mid = (l + r) >> 1;
    	if(ql <= mid) optadd(o<<1, l, mid, ql, qr, v);
    	if(mid < qr) optadd(o<<1|1, mid+1, r, ql, qr, v);
    	push_up(o);
    }
    
    void optmul(int o, int l, int r, int ql, int qr, int v) {
    	if(ql <= l && r <= qr) {
    		mulv[o] = (ll)(mulv[o] * v) % p;
    		addv[o] = (ll)(addv[o] * v) % p;
    		sumv[o] = (ll)(sumv[o] * v) % p;
    		return ;
    	}
    	push_down(o,l,r);
    	int mid = (l + r) >> 1;
    	if(ql <= mid) optmul(o<<1, l, mid, ql, qr, v);
    	if(mid < qr) optmul(o<<1|1, mid+1, r, ql, qr, v);
    	push_up(o);
    }
    
    ll query(int o, int l, int r, int ql, int qr) {
    	if(ql <= l && r <= qr) return (ll)sumv[o]%p;
    	push_down(o,l,r);
    	ll ans = 0;
    	int mid = (l + r) >> 1;
    	if(ql <= mid) ans = (ans + query(o<<1, l, mid, ql, qr) ) % p;
    	if(mid < qr) ans = (ans + query(o<<1|1, mid+1, r, ql, qr) ) % p;
    	return ans%p;
    }
    
    int main() {
    	scanf("%d%d%d",&n,&m,&p);
    	for(int i = 1; i <= n; i++) scanf("%lld",&a[i]);
    	build(1,1,n);
    	int tmp, l, r, v;
    	for(int i = 1; i <= m; i++) {
    		scanf("%d", &tmp);
    		if(tmp == 1) { scanf("%d%d%d", &l, &r, &v); optmul(1, 1, n, l, r, v); }
    		else if(tmp == 2) { scanf("%d%d%d", &l, &r, &v); optadd(1, 1, n, l, r, v); }
    		else if(tmp == 3) { scanf("%d%d", &l, &r); printf("%lld
    ", query(1, 1, n, l, r)); }
    	}
    	return 0;
    }
    /*
    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
    */
    
  • 相关阅读:
    wcf 基本配置
    Config 代码片段
    常用的中文字体
    C++创建一个新的进程
    C++ 线程学习
    [C++]多线程: 教你写第一个线程
    C++ 多线程编程实例【2个线程模拟卖火车票的小程序】
    C++使用thread类多线程编程
    C++多线程编程(★入门经典实例★)
    C++ 多线程
  • 原文地址:https://www.cnblogs.com/tyner/p/11237282.html
Copyright © 2020-2023  润新知