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

复健3--线段树

时间:2020-11-18 13:00:22      阅读:8      评论:0      收藏:0      [点我收藏+]

标签:追溯   线段树   二进制   完整   type   版本   基础   https   维护   

哦上帝啊,如果我有罪,请叫法律来制裁我,而不是写了个线段树调好几天,这篇复健鸽了两三天了(谢罪)因为最近在和学长聊天orz(我是什么臭鱼烂虾我这就爬)
个人觉得线段树的思想比树状数组要简单些,但是代码量确实是树状数组更优美orz
树状数组是从最基础元素开始标为1,用二进制整数下标存前缀和,但是线段树的话是从顶部节点开始标为1,每次用父节点2代表左子节点,父节点2+1代表右子节点,是一个完整形状的二叉树,别的应该都很好理解,主要是懒标记的处理,这就好比把操作单独存一个数组里,不每次都追溯到这个点本身,而是将对于该点的所有操作完成后对,当需要取出这个点的值的时候对这个点操作一次即可,这也正是线段树的优越之处
线段树大概是好在可以很方便的同时维护很多东西,代码的话这里就不放最基础版本的洛谷P3372线段树1了,直接放洛谷3373线段树2

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define l(x) t[x].l
#define r(x) t[x].r
#define sum(x) t[x].sum
#define add(x) t[x].add
#define mul(x) t[x].mul
const int maxn=100005;
struct tree {
	ll sum,mul,add;
	int l,r;
} t[maxn*4];
ll p,n,m;
int a[maxn];
void build(int x,int l,int r){
	l(x)=l,r(x)=r;
	mul(x)=1;
	if(l(x)==r(x)) {
		sum(x)=a[l];
		return;
	}
	int mid=(l+r)>>1;
	build(2*x,l,mid);
	build(2*x+1,mid+1,r);
	sum(x)=(sum(2*x)+sum(2*x+1))%p;
}
void update(int x){
	sum(2*x)=(sum(2*x)*mul(x)%p+(r(2*x)-l(2*x)+1)*add(x)%p)%p;
	sum(2*x+1)=(sum(2*x+1)*mul(x)%p+(r(2*x+1)-l(2*x+1)+1)*add(x)%p)%p;
	mul(2*x)=mul(2*x)*mul(x)%p;
	mul(2*x+1)=mul(2*x+1)*mul(x)%p;
	add(2*x)=add(2*x)*mul(x)%p;
	add(2*x+1)=add(2*x+1)*mul(x)%p;
	add(2*x)=(add(2*x)+add(x))%p;
	add(2*x+1)=(add(2*x+1)+add(x))%p;
	add(x)=0;
	mul(x)=1;
}
void change(int x,int l,int r,int z,int o){
	if (l(x)==l&&r(x)==r) {
		if(o==1) (sum(x)*=z)%=p,(mul(x)*=z)%=p,(add(x)*=z)%=p;
		else (sum(x)+=(r(x)-l(x)+1)*z)%=p,(add(x)+=z)%=p;
		return;
	}
	if(add(x)||mul(x)!=1) update(x);
	int mid=(l(x)+r(x))>>1;
	if(l>mid) change(2*x+1,l,r,z,o);
	else if(r<=mid) change(2*x,l,r,z,o);
	else {
		change(2*x,l,mid,z,o);
		change(2*x+1,mid+1,r,z,o);
	}
	sum(x)=(sum(2*x)+sum(2*x+1))%p;
}

ll query(int x,int l,int r){
	if(l(x)==l&&r(x)==r) return sum(x);
	if(add(x)||mul(x)!=1) update(x);
	ll tem=0;
	int mid=(l(x)+r(x))>>1;
	if(l>mid) tem+=query(2*x+1,l,r);
	else if(r<=mid) tem+=query(2*x,l,r);
	else tem=query(2*x,l,mid)+query(2*x+1,mid+1,r);
	return tem%p;
}
int main() {
	scanf("%lld%lld%lld",&n,&m,&p);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	build(1,1,n);
	for(int i=1;i<=m;i++){
		int opt,x,y,z;
		scanf("%d%d%d",&opt,&x,&y);
		if(opt!=3) {
			scanf("%d",&z);
			change(1,x,y,z,opt);
		}
		else printf("%lld\n",query(1,x,y));	
	}
}```

复健3--线段树

标签:追溯   线段树   二进制   完整   type   版本   基础   https   维护   

原文地址:https://www.cnblogs.com/maniac731/p/13966398.html

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