简介
线段树是一种可以用来维护区间信息常用的数据结构。线段树可以在的时间复杂度完成单点修改、区间修改、区间查询(区间求和,求区间最大值,求区间最小值)等操作。
查询时我们使用懒标记,当要用到时,再给他加上。
学当初学了一点,但是并没有完全掌握,现在重新学一下,毕竟比赛的时候就遇到了线段树的题目,结果一题都不会写。
主要是写的线段树的题目太少了,根本不可能做到灵活运用。
先上一个板子
const int N=100010struct Node{int l, r;// TODO: 需要维护的信息和懒标记}tr[N * 4];void pushup(int u){// TODO: 利用左右儿子信息维护当前节点的信息}void pushdown(int u){// TODO: 将懒标记下传}void build(int u, int l, int r){if (l == r) tr[u] = {l, r};else{tr[u] = {l, r};int mid = l + r >> 1;build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);pushup(u);}}void update(int u, int l, int r, int d){if (tr[u].l >= l && tr[u].r <= r){// TODO: 修改区间}else{pushdown(u);int mid = tr[u].l + tr[u].r >> 1;if (l <= mid) update(u << 1, l, r, d);if (r > mid) update(u << 1 | 1, l, r, d);pushup(u);}}int query(int u, int l, int r){if (tr[u].l >= l && tr[u].r <= r){return ; // TODO 需要补充返回值}else{pushdown(u);int mid = tr[u].l + tr[u].r >> 1;int res = 0;if (l <= mid ) res = query(u << 1, l, r);if (r > mid) res += query(u << 1 | 1, l, r);return res;}}
模板题
洛谷P3372
区间更新,区间查询,在一段区间加上一个数,查询一段区间的和。
#include<bits/stdc++.h>using namespace std;typedef long long ll;const int N=1000010;ll a[N],sum[N<<2],lazy[N<<4]; //树状数组长度最大不会超过4*n//构造线段树void build(int rt,int l,int r){if(l==r){sum[rt] = a[l];lazy[rt] = 0;return;}//递归构造左右子树int mid=l+r>>1;build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);sum[rt]=sum[rt<<1] + sum[rt<<1|1];//把父区间[l,r],分成左右两个[l,mid],(mid,r]区间。}//向下传递void push_down(int rt,int l,int r){int mid=l+r>>1;if(lazy[rt]){sum[rt<<1]+=lazy[rt]*(mid-l+1);//维护区间和,如果是别的可以改sum[rt<<1|1]+=lazy[rt]*(r-mid);lazy[rt<<1]+=lazy[rt];lazy[rt<<1|1]+=lazy[rt];//懒标记的值传递lazy[rt]=0;}}//更新值void update(int rt,int l,int r,int L,int R,int z){int mid=l+r>>1;if(L<=l&&r<=R){sum[rt]+=z*(r-l+1);//维护区间和,如果是别的可以改lazy[rt]+=z;return;}push_down(rt,l,r);if(L<=mid) update(rt<<1,l,mid,L,R,z);if(R>mid) update(rt<<1|1,mid+1,r,L,R,z);sum[rt]=sum[rt<<1]+sum[rt<<1|1];}//查询值ll query(int rt,int l,int r,int L,int R){int mid=l+r>>1;if(L<=l&&r<=R){return sum[rt];}push_down(rt,l,r);ll res=0;if(L<=mid) res+=query(rt<<1,l,mid,L,R);if(R>mid) res+=query(rt<<1|1,mid+1,r,L,R);return res;}int main(){int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%lld",&a[i]);}build(1,1,n);while(m--){int op;scanf("%d",&op);if(op&1){int x,y,z;scanf("%d%d%d",&x,&y,&z);update(1,1,n,x,y,z);}else{int x,y;scanf("%d%d",&x,&y);printf("%lld\n",query(1,1,n,x,y));}}return 0;}
P3373
区间乘上一个数,求一段区间的和
代码:
#include<bits/stdc++.h>using namespace std;#define int long longconst int N=100010;int mod;int sum[N<<2],mul[N<<2],lazy[N<<2],a[N];void push_up(int rt){sum[rt]=(sum[rt<<1]+sum[rt<<1|1])%mod;}void push_down(int rt,int l,int r){int mid=l+r>>1;if(mul[rt]!=1){mul[rt<<1]=(mul[rt<<1]*mul[rt])%mod;mul[rt<<1|1]=(mul[rt<<1|1]*mul[rt])%mod;lazy[rt<<1]=(lazy[rt<<1]*mul[rt])%mod;lazy[rt<<1|1]=(lazy[rt<<1|1]*mul[rt])%mod;sum[rt<<1]=(sum[rt<<1]*mul[rt])%mod;sum[rt<<1|1]=(sum[rt<<1|1]*mul[rt])%mod;mul[rt]=1;}if(lazy[rt]){sum[rt<<1]=(sum[rt<<1]+lazy[rt]*(mid-l+1))%mod;sum[rt<<1|1]=(sum[rt<<1|1]+lazy[rt]*(r-mid))%mod;lazy[rt<<1]=(lazy[rt<<1]+lazy[rt])%mod;lazy[rt<<1|1]=(lazy[rt<<1|1]+lazy[rt])%mod;lazy[rt]=0;}}void build(int rt,int l,int r){mul[rt]=1;if(l==r){sum[rt]=a[l];return;}int mid=l+r>>1;build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);push_up(rt);}void update1(int rt,int l,int r,int L,int R,int d){if(L<=l && r<=R){mul[rt]=mul[rt]*d%mod;lazy[rt]=lazy[rt]*d%mod;sum[rt]=sum[rt]*d%mod;return;}push_down(rt,l,r);int mid=l+r>>1;if(L<=mid) update1(rt<<1,l,mid,L,R,d);if(R>mid) update1(rt<<1|1,mid+1,r,L,R,d);push_up(rt);}void update2(int rt,int l,int r,int L,int R,int d){if(L<=l && r<=R){sum[rt]=(sum[rt]+d*(r-l+1))%mod;lazy[rt]=(lazy[rt]+d)%mod;return;}push_down(rt,l,r);int mid=l+r>>1;if(L<=mid) update2(rt<<1,l,mid,L,R,d);if(R>mid) update2(rt<<1|1,mid+1,r,L,R,d);push_up(rt);}int query(int rt,int l,int r,int L,int R){if(L<=l&& r<=R) return sum[rt];push_down(rt,l,r);int res=0;int mid=l+r>>1;if(L<=mid) res+=query(rt<<1,l,mid,L,R);res%=mod;if(R>mid) res+=query(rt<<1|1,mid+1,r,L,R);return res%mod;}signed main(){int n,m,p;cin>>n>>m>>mod;for(int i=1;i<=n;i++){cin>>a[i];}build(1,1,n);while(m--){int op,l,r,d;cin>>op;if(op==1){cin>>l>>r>>d;update1(1,1,n,l,r,d);}else if(op==2){cin>>l>>r>>d;update2(1,1,n,l,r,d);}else if(op==3){cin>>l>>r;cout<<query(1,1,n,l,r)<<endl;}}return 0;}
维护区间最小值
#include <bits/stdc++.h>
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int N = 5e5 + 7;
const int M = 1e6 + 7;
const int mod = 1e9 + 7;
const double eps = 1e-6;
ll t, n, k,m;
int a[N],tree[N<<2];
void build(int rt,int l,int r){
if(l==r){
tree[rt]=a[l];
return;
}
int mid=(l+r)>>1;
build(rt<<1,l,mid);
build(rt<<1|1,mid+1,r);
tree[rt]=min(tree[rt<<1],tree[rt<<1|1]);
}
void update(int rt,int l,int r,int x,int y){
if(l==r){
tree[rt]=y;
return;
}
int mid=(l+r)>>1;
if(x<=mid){
update(rt<<1,l,mid,x,y);
}
else{
update(rt<<1|1,mid+1,r,x,y);
}
tree[rt]=min(tree[rt<<1],tree[rt<<1|1]);
}
int query(int rt,int l,int r,int L,int R){
if(L<=l&&r<=R){
return tree[rt];
}
int mid=(l+r)>>1;
int ans=1e9+10;
if(L<=mid) ans=min(ans,query(rt<<1,l,mid,L,R));
if(R>mid) ans=min(ans,query(rt<<1|1,mid+1,r,L,R));
return ans;
}
void solve() {
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,1,n);
while(m--){
int op,x,y;
cin>>op;
if(op==1){
cin>>x>>y;
update(1,1,n,x,y);
a[x]=y;
}
else{
cin>>x;
int al,ar,l=1,r=x;
while(l<r){
//TODO
int mid=(l+r)>>1;
if(query(1,1,n,mid,x)>=a[x]) r=mid;
else l=mid+1;
}
al=x-l+1;
l=x,r=n+1;
while(l<r){
int mid=(l+r)>>1;
if(query(1,1,n,x,mid)<a[x]) r=mid;
else l=mid+1;
}
l--;
ar=l-x+1;
cout<<1ll*al*ar<<endl;
}
}
}
signed main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
t=1;
//cin >> t;
while(t--) solve();
return 0;
}
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
const int INF=0x3f3f3f3f;
int a[N],mn[N<<2],need[N<<2],he[N],res=1,h=0;
void update(int rt,int l,int r,int x,int val){
if(l==r){
mn[rt]=val;
return;
}
int mid=l+r>>1;
if(x<=mid) update(rt<<1,l,mid,x,val);
if(x>mid) update(rt<<1|1,mid+1,r,x,val);
mn[rt]=min(mn[rt<<1],mn[rt<<1|1]);
}
int query(int rt,int l,int r,int x,int y){
if(x<=l&&r<=y) return mn[rt];
int mid=l+r>>1,ans=INF;
if(x<=mid) ans=min(query(rt<<1,l,mid,x,y),ans);
if(y>mid) ans=min(query(rt<<1|1,mid+1,r,x,y),ans);
return ans;
}
int main(){
int n,k;
cin>>k>>n;
for(int i=0;i<n;i++){
int x,y;
cin>>x>>y;
if(i<k){//前面k个,先初始化
update(1,0,k-1,i,x+y);
he[i]++;
continue;
}
if(mn[1]>x) continue;//如果当前所有都不能满足,直接跳过
int d=i%k,l,r,ans;
int tot=query(1,0,k-1,d,k-1);
if(tot<=x){
l=d,r=k-1;//i%k的后半段
}
else{//i%k的前半段
l=0,r=d-1;
}
//二分查找区间长度,确定位置最靠近左边的合适的位置
while(l<=r){
int mid=l+r>>1;
if(query(1,0,k-1,l,mid)<=x) ans=mid,r=mid-1;
else l=mid+1;
}
update(1,0,k-1,ans,x+y);
he[ans]++;
res=max(res,he[ans]);//记录处理最多事件的数量
//printf("res = %d\n",res);
}
for(int i=0;i<k;i++){
//printf("%d ",he[i]);
if(he[i]==res) need[++h]=i;
}
//puts("");
for(int i=1;i<=h;i++){
if(i!=1) printf(" ");
printf("%d",need[i]);
}
}
