复健3--线段树
Posted maniac731
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了复健3--线段树相关的知识,希望对你有一定的参考价值。
哦上帝啊,如果我有罪,请叫法律来制裁我,而不是写了个线段树调好几天,这篇复健鸽了两三天了(谢罪)因为最近在和学长聊天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
",query(1,x,y));
}
}```
以上是关于复健3--线段树的主要内容,如果未能解决你的问题,请参考以下文章