动态开点线段树球调
查看原帖
动态开点线段树球调
344405
曹操废了楼主2022/5/13 20:35

RT,马蜂良好,注释详细

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<string>
#include<map>
#include<queue>
#include<stack>
#include<time.h>
#define INF 0x3f3f3f3f
#define int long long
#define MAXN 400001
#define ls(x) f[x].l //左儿子
#define rs(x) f[x].r //右儿子 
using namespace std;
int n,m,mod;//膜数
int add[MAXN];//加的lazy标记
int mul[MAXN];//乘的lazy标记
struct kx{
	int l,r;//左儿子,右儿子编号
	int v;//区间和
}f[MAXN];//动态开点线段树
int gen;//根
void up(int p){//算编号为q的区间和
	f[p].v=(f[ls(p)].v+f[rs(p)].v)%mod;//左儿子区间和+右儿子区间和
}
void down(int p,int l,int r){//标记下传,编号,左端点,右端点。
	if(add[p]||mul[p]!=1){//如果有lazy标记
		int m=(l+r)/2;//取中间点

		mul[ls(p)]=(mul[ls(p)]*mul[p])%mod;//先传乘
		add[ls(p)]=(add[ls(p)]*mul[p])%mod;//传加,要算上乘lazy标记
		add[ls(p)]=(add[ls(p)]+add[p])%mod;//传加
		f[ls(p)].v=(f[ls(p)].v*mul[p])%mod;//更新区间和(乘)
		f[ls(p)].v=(f[ls(p)].v+(m-l+1)*add[p])%mod;//更新区间和(加)

		mul[rs(p)]=(mul[rs(p)]*mul[p])%mod;//先传乘
		add[rs(p)]=(add[rs(p)]*mul[p])%mod;//传加,要算上乘lazy标记
		add[rs(p)]=(add[rs(p)]+add[p])%mod;//传加
		f[rs(p)].v=(f[rs(p)].v*mul[p])%mod;//更新区间和(乘)
		f[rs(p)].v=(f[rs(p)].v+(r-m)*add[p])%mod;//更新区间和(加)

		mul[p]=1;
		add[p]=0;//清空lazy标记
	}
}
void build(int &p,int l,int r){//编号,左端点,右端点(建树)
	add[p]=0,mul[p]=0;//清空lazy标记
	if(l==r){
		scanf("%lld",&f[p].v);
		return ;
	}
	int m=(l+r)/2;//取中间点
	build(ls(p),l,m);//递归左儿子
	build(rs(p),m+1,r);//递归右儿子
	up(p);//更新区间和
}
void update(int p,int l,int r,int x,int y,int val,int c){
	//编号,左端点,右端点,左端点(运算),右端点(运算),运算的值,运算类型(1为加,2为乘)
	if(y<l||x>r) return ;//当前区间与运算区间完全不重叠
	if(x<=l&&r<=y){//当前区间被运算区间完全包含
		if(c==1){//加
			add[p]=(add[p]+val)%mod;//打上lazy标记(加)
			f[p].v=(f[p].v+(long long)(r-l+1)*val)%mod;//更新区间和
		}else{//乘
			add[p]=(add[p]*val)%mod;//打上lazy标记(加)
			mul[p]=(mul[p]*val)%mod;//打上lazy标记(乘)
			f[p].v=(f[p].v*val)%mod;//更新区间和
		}
		return ;
	}
	down(p,l,r);//标记下传
	int m=(l+r)/2;//取中点
	update(ls(p),l,m,x,y,val,c);//递归左端点
	update(rs(p),m+1,r,x,y,val,c);//递归右端点
	up(p);//更新区间和
}
int query_sum(int p,int l,int r,int x,int y){//查询区间和
	//编号,左端点,右端点,左端点(查询),右端点(查询)
	if(y<l||x>r) return 0;//当前区间与查询区间完全不重叠
	if(x<=l&&r<=y){//当前区间完全被查询区间包含
		return f[p].v;
	}
	down(p,l,r);//标记下传
	int m=(l+r)/2;//取中点
	int ret=0;
	ret=(ret+query_sum(ls(p),l,m,x,y))%mod;//加上左端点区间和
	ret=(ret+query_sum(rs(p),m+1,r,x,y))%mod;//加上右端点区间和
	return ret;

}
signed main(){
	ios::sync_with_stdio(false); 
	srand(time(NULL));
	cin>>n>>mod;
	build(gen,1,n);//建树
	cin>>m;
	for(int i=1,opd,l,r,val;i<=m;i++){
		cin>>opd;//运算类型
		if(opd==1){//乘
			cin>>l>>r>>val;//左端点,右端点,运算的值
			update(1,1,n,l,r,val,2);//修改
		}else if(opd==2){//加
			cin>>l>>r>>val;//左端点,右端点,运算的值
			update(1,1,n,l,r,val,1);//修改
		}else{
			cin>>l>>r;//左端点,右端点
			cout<<query_sum(1,1,n,l,r)<<"\n";//查询
		}
	}
	return 0;
}
2022/5/13 20:35
加载中...