数据结构-模板-线段树
线段树我闭着眼都能打
这里先是废话。线段树是一个有些整人心态的数据结构,,,以后应该会更多我应该算是理解,但这是在心里默认线段树是绝对正确的情况下,按照脑补去理解。至今我的脑部还未被推翻所以我就认为是对的,虽然很不严谨就是了。但毕竟我也就一普通人。
好了,接下来谈谈线段树。线段树能够以O(n)时间构造。每次操作,包括但不仅限于单点查询,单点修改,区间查询,区间修改,可以在O(logn)的时间内处理。
是个平衡二叉树很好理解(大概?),也正是因为它是平衡二叉树,所以它单点修改和单点查询和区间查询可以在O(logn)内完成,但是对于区间修改需要依赖于lazytag数组来缩短至O(logn),想想极限情况下修改[1,n]范围内的数据。
接下来直接放上线段树维护区间和的代码,修改操作仅限于区间加。
对应洛谷的题:https://www.luogu.com.cn/problem/P3372?contestId=43385
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
typedef long long ll;
typedef unsigned long long ull;
const ll MAXN=1e18;
const int MOD=1e6;
struct SegmentTree{
ll date[400050];
ll tag[400050];
#define lson rt<<1 //左孩子
#define rson rt<<1|1 //右孩子
void push_up(int rt){
date[rt]=date[lson]+date[rson]; //更新date[rt]
}
//依靠push_down函数实现lazy_tag的向下传递。
void push_down(int rt,int lef,int rig){
if(tag[rt]){
int mid=(lef+rig)>>1;
tag[lson]+=tag[rt];
tag[rson]+=tag[rt];
date[lson]+=(mid-lef+1)*tag[rt];
date[rson]+=(rig-mid)*tag[rt];
tag[rt]=0;
}
}
//构造树
void init(int lef,int rig,int rt){
if(lef==rig) {cin>>date[rt];return;}
int mid=(lef+rig)>>1;
init(lef,mid,lson);
init(mid+1,rig,rson);
push_up(rt);
}
//区间更新,区间加的方法和lazy_tag的传递是相互独立的。直到需要修改的最底层才合并它们
//a,b时修改的区间,c是增加的值。,rt是当前位置,rig和lef是当前所在位置对应的区间端点,查询的参数差不多同理。
void update(int lef,int rig,int rt,int a,int b,ll c){
if(a<=lef&&b>=rig){//到需要修改的最底层才进行修改,然后返回修改路径上节点的值(它的父节点,父节点的父节点)。
date[rt]+=(rig-lef+1)*c;
tag[rt]+=c;//合并lazytag和区间加
return;
}
push_down(rt,lef,rig);//向下传递lazytag,目的是在达到需要修改的最底层前把路劲上的值依照lazytag全都修改
int mid=(lef+rig)>>1;
if(a<=mid) update(lef,mid,lson,a,b,c);
if(b>mid) update(mid+1,rig,rson,a,b,c);
push_up(rt);//rt位置的值修改依赖于左右孩子的值的修改
}
ll query(int lef,int rig,int rt,int a,int b){
if(a<=lef&&b>=rig) return date[rt];
push_down(rt,lef,rig);//向下传递lazytag,目的是在达到需要修改的最底层前把路劲上的值依照lazytag全都修改
long long ans=0;
int mid=(lef+rig)>>1;
if(a<=mid) ans+=query(lef,mid,lson,a,b);
if(b>mid) ans+=query(mid+1,rig,rson,a,b);
return ans;
}
}tree;
int main(){
int n,m;
cin>>n>>m;
tree.init(1,n,1);
int op;
int x,y;
ll tmp;
while(m--){
cin>>op;
if(op==1){
cin>>x>>y>>tmp;
tree.update(1,n,1,x,y,tmp);//更新操作
}
else if(op==2){
cin>>x>>y;
cout<<tree.query(1,n,1,x,y)<<'\n';//查询操作
}
}
return 0;
}
然后是区间加+区间乘法,有点恶心。
大体思路是把用两个lazy_tag维护乘法和加法。
对应的洛谷的题 https://www.luogu.com.cn/problem/P3373?contestId=43385
代码
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
typedef long long ll;
typedef unsigned long long ull;
const ll MAXN=1e18;
const int MOD=1e6;
int p;
struct SegmentTree{
ll date[400050];
ll tag_add[400050];
ll tag_time[400050];
#define lson rt<<1
#define rson rt<<1|1
void push_up(int rt){
date[rt]=(date[lson]+date[rson])%p;
}
//密密麻麻地,,,
//核心代码,核心代码,核心代码。
//父节点和两儿子节点的lazytag相遇,把加法拿出来,乘法就直接乘。大概是要有num=num*time+add的感觉。其它都大同小异不多赘述。
void push_down(int rt,int lef,int rig){
if(tag_add[rt]==0&&tag_add[rt]==1) return;
int mid=(lef+rig)>>1;
tag_add[lson]=tag_add[lson]*tag_time[rt]%p+tag_add[rt]%p;
tag_time[lson]=tag_time[lson]*tag_time[rt]%p;
tag_add[rson]=tag_add[rson]*tag_time[rt]%p+tag_add[rt]%p;
tag_time[rson]=tag_time[rson]*tag_time[rt]%p;
date[lson]=date[lson]*tag_time[rt]%p+(mid-lef+1)*tag_add[rt]%p;
date[rson]=date[rson]*tag_time[rt]%p+(rig-mid)*tag_add[rt]%p;
tag_add[rt]=0;tag_time[rt]=1;
}
void init(int lef,int rig,int rt){
tag_time[rt]=1;
if(lef==rig) {cin>>date[rt];return;}
int mid=(lef+rig)>>1;
init(lef,mid,lson);
init(mid+1,rig,rson);
push_up(rt);
}
void update_add(int lef,int rig,int rt,int a,int b,ll c){
if(a<=lef&&b>=rig){
date[rt]=(date[rt]+(rig-lef+1)*c%p)%p;
tag_add[rt]=(tag_add[rt]+c)%p;////和push_down差不多的道理
return;
}
push_down(rt,lef,rig);
int mid=(lef+rig)>>1;
if(a<=mid) update_add(lef,mid,lson,a,b,c);
if(b>mid) update_add(mid+1,rig,rson,a,b,c);
push_up(rt);
}
void update_time(int lef,int rig,int rt,int a,int b,ll c){
if(a<=lef&&b>=rig){
date[rt]=date[rt]*c%p;
tag_add[rt]=tag_add[rt]*c%p;//和push_down差不多的道理
tag_time[rt]=tag_time[rt]*c%p;
return;
}
push_down(rt,lef,rig);
int mid=(lef+rig)>>1;
if(a<=mid) update_time(lef,mid,lson,a,b,c);
if(b>mid) update_time(mid+1,rig,rson,a,b,c);
push_up(rt);
}
ll query(int lef,int rig,int rt,int a,int b){
if(a<=lef&&b>=rig) return date[rt]%p;
push_down(rt,lef,rig);
long long ans=0;
int mid=(lef+rig)>>1;
if(a<=mid) ans+=query(lef,mid,lson,a,b);
if(b>mid) ans+=query(mid+1,rig,rson,a,b);
return ans%p;
}
}tree;
int main(){
int n,m;
cin>>n>>m>>p;
tree.init(1,n,1);
int op;
int x,y;
ll tmp;
while(m--){
cin>>op;
if(op==1){
cin>>x>>y>>tmp;
tree.update_time(1,n,1,x,y,tmp%p);
}
else if(op==2){
cin>>x>>y>>tmp;
tree.update_add(1,n,1,x,y,tmp%p);
}
else{
cin>>x>>y;
cout<<tree.query(1,n,1,x,y)<<'\n';
}
}
return 0;
}
emmmm...常数就挺大的。