线段树的五种操作
线段树的结构(数组开4倍)
① 建立线段树 build
② 查询操作 query
判断查询区间[L,R]和线段树区间[TL,TR]的关系。如果线段树区间被查询区间包含,则直接返回需要的信息,如果交叉,则分情况对左右区间进行递归。
③ 修改操作 modify
单点修改,递归到对应的叶节点修改,然后pushup即可
区间修改,使用last标记
④ 更新操作
pushup(u) 将子节点的信息更新到父节点
pushdown(u) 搭配lazy标记,将子节点的更新记录在父节点,等查询时再递归将lazy标记更新到子节点,然后清空父节点的lazy标记
最大数(单点修改,区间查询)
维护区间最大值
struct node{
int l,r;
ll maxn;
}tr[N<<2];
ll m,p;
int n;
void pushup(int u){
tr[u].maxn=max(tr[u<<1].maxn,tr[u<<1|1].maxn);
}
void build(int u,int l,int r){
if(l==r){
tr[u].l=l;
tr[u].r=r;
}else{
tr[u].l=l;
tr[u].r=r;
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);//只在非叶节点pushup
}
}
//单点修改
void modify(int u,int x,ll c){
if(tr[u].l==tr[u].r&&tr[u].l==x){
tr[u].maxn=c;
}else{
int mid=tr[u].l+tr[u].r>>1;
if(x<=mid) modify(u<<1,x,c);
else modify(u<<1|1,x,c);
pushup(u);//只在非叶节点pushup
}
}
//区间查询
ll query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r) return tr[u].maxn;
int mid=tr[u].l+tr[u].r>>1;
ll res=-1;
if(l<=mid) res=max(res,query(u<<1,l,r));
if(r>mid) res=max(res,query(u<<1|1,l,r));
return res;
}
int main(){
cin>>m>>p;
build(1,1,m);//最多有m个点
ll last=0;
while(m--){
char c;cin>>c;
if(c=='Q'){
int l;cin>>l;
last=query(1,n-l+1,n);
cout<<last<<endl;
}else{
ll t;cin>>t;
t=(t+last)%p;
modify(1,++n,t);
}
}
}
你能回答这些问题吗(单点修改,区间查询)
维护区间的最大左前缀和以及最大右前缀和
struct Node{
int l,r;
int sum;//区间总和
int lmax;//最大前缀和
int rmax;//最大后缀和
int tmax;//连续序列和
}tree[N<<2];
void pushup(Node &u,Node &l,Node &r){
u.sum=l.sum+r.sum;
u.lmax=max(l.lmax,l.sum+r.lmax);
u.rmax=max(r.rmax,r.sum+l.rmax);
u.tmax=max(max(l.tmax,r.tmax),l.rmax+r.lmax);
}
void pushup(int u){
pushup(tree[u],tree[u<<1],tree[u<<1|1]);
}
void build(int u,int l,int r){
if(l==r) tree[u]={l,r,a[l],a[l],a[l],a[l]};
else
{
tree[u].l=l,tree[u].r=r;
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
}
void modify(int u,int x,int y){
if(tree[u].l==x&&tree[u].r==x)
tree[u]={x,x,y,y,y,y};
else{
int mid=tree[u].l+tree[u].r>>1;
if(x<=mid) modify(u<<1,x,y);
else modify(u<<1|1,x,y);
pushup(u);
}
}
Node query(int u,int l,int r){
if(tree[u].l>=l&&tree[u].r<=r) return tree[u];
else{
int mid=tree[u].l+tree[u].r>>1;
if(r<=mid) return query(u<<1,l,r);
else if(l>mid) return query(u<<1|1,l,r);
else{
Node left=query(u<<1,l,r);
Node right=query(u<<1|1,l,r);
Node res;//利用pushup计算左右区间的连续区间和
pushup(res,left,right);
return res;
}
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
build(1,1,n);
while(m--){
int k,x,y;cin>>k>>x>>y;
if(k&1){
if(x>y) swap(x,y);
cout<<query(1,x,y).tmax<<endl;
}
else{
modify(1,x,y);
}
}
}
区间最大公约数(区间修改,区间查询)
① 维护差分数组,将区间修改变为单点修改
② 维护区间的最大公约数,利用原数组的最大公约数就等于差分数组的最大公约数的性质
查询[l,r]的最大公约数时,
所以我们先求出差分中b[l+1]~b[r]的最大公约数,即query(1,l+1,r),然后和a[l]再求一次最大公约数即为区间的最大公约数,由于a[l]可能被修改,所以使用query(1,1,l).sum求出当前的a[l],答案即为
#include<iostream>
#include<algorithm>
using namespace std;
const int N=500010;
typedef long long ll;
struct Node{
int l,r;
ll sum;//区间和
ll d; //区间最大公约数
}tr[4*N];
ll gcd(ll a,ll b){
return b?gcd(b,a%b):a;
}
int n,m;
ll w[N];
void pushup(Node &u,Node &l,Node &r){
u.sum=l.sum+r.sum;
u.d=gcd(l.d,r.d);
}
void pushup(int u){
pushup(tr[u],tr[u<<1],tr[u<<1|1]);
}
void build(int u,int l,int r){
if(l==r){
tr[u]={l,r,w[l]-w[l-1],w[l]-w[l-1]};
return;
}
tr[u]={l,r};
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
void modify(int u,int x,ll v){
if(tr[u].l==tr[u].r&&tr[u].l==x){
tr[u].sum+=v;
tr[u].d+=v;
return;
}
int mid=tr[u].l+tr[u].r>>1;
if(x<=mid) modify(u<<1,x,v);
if(x>mid) modify(u<<1|1,x,v);
pushup(u);
}
Node query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r) return tr[u];
int mid=tr[u].l+tr[u].r>>1;
if(r<=mid) return query(u<<1,l,r);
else if(l>mid) return query(u<<1|1,l,r);
else{
Node left=query(u<<1,l,r);
Node right=query(u<<1|1,l,r);
Node res;
pushup(res,left,right);
return res;
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>w[i];
build(1,1,n);
while(m--){
string op;cin>>op;
if(op=="C"){
int l,r;ll d;cin>>l>>r>>d;
modify(1,l,d);
if(r+1<=n)
modify(1,r+1,-d);
}
else{
int l,r;cin>>l>>r;
if(l==r){//区间只有一个数
Node a=query(1,1,l);
cout<<abs(a.sum)<<endl;
continue;
}
Node a=query(1,1,l),b=query(1,l+1,r);
ll res=abs(gcd(a.sum,b.d));
cout<<res<<endl;
}
}
}
一个简单的整数问题2(区间修改,区间查询)
维护区间和,用lazy标记记录更新信息
注意使用lazy标记时,pushdown完要清空父节点lazy标记,pushdown需要在modify中分区间修改以及query分区间查询时使用
struct node{
int l,r;
ll sum;
int lazy;
}tr[N<<2];
void pushup(int u){
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}
//父节点lazy标记传递,更新子节点信息
void pushdown(int u){
node &root=tr[u];
node &left=tr[u<<1];
node &right=tr[u<<1|1];
left.sum+=(ll)(left.r-left.l+1)*root.lazy;
left.lazy+=root.lazy;
right.sum+=(ll)(right.r-right.l+1)*root.lazy;
right.lazy+=root.lazy;
root.lazy=0;//清空父节点lazy
}
void build(int u,int l,int r){
if(l==r){
tr[u]={l,r,w[l]};
}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 modify(int u,int l,int r,int d){
if(tr[u].l>=l&&tr[u].r<=r){
tr[u].sum+=(ll)(tr[u].r-tr[u].l+1)*d;
tr[u].lazy+=d;
return;
}
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid) modify(u<<1,l,r,d);
if(r>mid) modify(u<<1|1,l,r,d);
pushup(u);
}
//区间查询
ll query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r) return tr[u].sum;
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
ll res=0;
if(l<=mid) res+=query(u<<1,l,r);
if(r>mid) res+=query(u<<1|1,l,r);
return res;
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>w[i];
build(1,1,n);
while(m--){
string op;cin>>op;
if(op=="Q"){
ll l,r;cin>>l>>r;
cout<<query(1,l,r)<<endl;
}
else{
ll l,r,d;cin>>l>>r>>d;
modify(1,l,r,d);
}
}
}
维护序列(区间修改,区间查询)
维护区间和,但要分别利用两个懒标记来处理区间加和区间乘的更新信息
struct node{
int l,r;
ll sum;//区间总和
ll add;//加的懒标记
ll mul;//乘的懒标记
}tr[N<<2];
void pushup(int u){
tr[u].sum=(tr[u<<1].sum+tr[u<<1|1].sum)%p;
}
//传递lazy标记,更新子节点
void pushdown(int u){
node &root=tr[u];
node &left=tr[u<<1];
node &right=tr[u<<1|1];
left.sum=(ll)left.sum*root.mul%p;
left.sum=(left.sum+(left.r-left.l+1)*root.add)%p;
left.add=(left.add*root.mul+root.add)%p;
left.mul=(left.mul*root.mul)%p;
right.sum=(ll)right.sum*root.mul%p;
right.sum=(right.sum+(right.r-right.l+1)*root.add)%p;
right.add=(right.add*root.mul+root.add)%p;
right.mul=(right.mul*root.mul)%p;
root.add=0;
root.mul=1;
}
void build(int u,int l,int r){
if(l==r) tr[u]={l,r,a[l],0,1};
else{
tr[u]={l,r,0,0,1};
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
}
//区间修改
void modify(int u,int l,int r,int add,int mul){
if(tr[u].l>=l&&tr[u].r<=r){
tr[u].sum=(ll)tr[u].sum*mul%p;
tr[u].sum=(tr[u].sum+(tr[u].r-tr[u].l+1)*add)%p;
tr[u].add=(tr[u].add*mul+add)%p;
tr[u].mul=(tr[u].mul*mul)%p;
return;
}
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid) modify(u<<1,l,r,add,mul);
if(r>mid) modify(u<<1|1,l,r,add,mul);
pushup(u);
}
//区间查询
ll query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r) return tr[u].sum;
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
ll res=0;
if(l<=mid) res+=query(u<<1,l,r);
if(r>mid) res+=query(u<<1|1,l,r);
return res%p;
}
int main(){
cin>>n>>p;
for(int i=1;i<=n;i++) cin>>a[i];
build(1,1,n);
int m;cin>>m;
while(m--){
int t;cin>>t;
if(t==1){
int a,b,c;cin>>a>>b>>c;
modify(1,a,b,0,c);
}
else if(t==2){
int a,b,c;cin>>a>>b>>c;
modify(1,a,b,c,1);
}
else{
int a,b;cin>>a>>b;
cout<<query(1,a,b)<<endl;
}
}
}
油漆面积(扫描线)
对于每个矩形的左右边,左边入边,右边为出边,对于矩形(x1,y1,x2,y2),两条边表示为(x1,y1,y2,1)(x2,y1,y2,-1)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=1e4+5;
struct segment{
int x,y1,y2;
int k;//k=1/2 左/右侧边
bool operator<(const segment &t)const{
return x<t.x;
}
}seg[N*2];//n个矩形,2*n个线段
struct node{
int l,r;
int cnt;//区间被覆盖次数
int len;//被覆盖区间的长度
}tr[N*4];
void pushup(int u){
if(tr[u].cnt>0){
tr[u].len=tr[u].r-tr[u].l+1;
}else if(tr[u].l==tr[u].r){
tr[u].len=0;
}else{
tr[u].len=tr[u<<1].len+tr[u<<1|1].len;
}
}
void build(int u,int l,int r){
if(l==r) tr[u]={l,r,0,0};
else{
tr[u]={l,r};
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
tr[u].cnt=0;
tr[u].len=0;
}
}
void modify(int u,int l,int r,int k){
if(tr[u].l>=l&&tr[u].r<=r){
tr[u].cnt+=k;
pushup(u);
return;
}
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid) modify(u<<1,l,r,k);
if(r>mid) modify(u<<1|1,l,r,k);
pushup(u);
}
int main(){
int n;cin>>n;
int m=0;
for(int i=1;i<=n;i++){
int x1,x2,y1,y2;
cin>>x1>>y1>>x2>>y2;
seg[m++]={x1,y1,y2,1};
seg[m++]={x2,y1,y2,-1};
}
sort(seg,seg+m);
build(1,0,10000);//0<=x<=10000
int res=0;
//逐个矩形边处理
for(int i=0;i<m;i++){
//根节点的len 表示两个相邻竖边内被阴影部分覆盖的总高度
if(i>0) res+=tr[1].len*(seg[i].x-seg[i-1].x);
modify(1,seg[i].y1,seg[i].y2-1,seg[i].k);
}
cout<<res;
}
亚特兰蒂斯(扫描线)
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
typedef long long ll;
int n;
struct segment{
double x,y1,y2;
int k;
bool operator<(const segment &t)const{
return x<t.x;
}
}seg[N*2];
struct node{
int l,r;
int cnt;
double len;
}tr[N*8];//4*2N
vector<double> ys;//y轴坐标离散化
//返回vector 中第一个 >= y 的数的下标
int find(double y){
return lower_bound(ys.begin(), ys.end(), y) - ys.begin();
}
void pushup(int u){
if(tr[u].cnt){
tr[u].len=ys[tr[u].r+1]-ys[tr[u].l];
}else if(tr[u].l==tr[u].r){
tr[u].len=0;
}else{
tr[u].len=tr[u<<1].len+tr[u<<1|1].len;
}
}
void build(int u,int l,int r){
if(l==r) tr[u]={l,r,0,0};
else{
tr[u]={l,r};
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
tr[u].cnt=0;
tr[u].len=0;
}
}
void modify(int u,int l,int r,int k){
if(tr[u].l>=l&&tr[u].r<=r){
tr[u].cnt+=k;
pushup(u);
return;
}
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid) modify(u<<1,l,r,k);
if(r>mid) modify(u<<1|1,l,r,k);
pushup(u);
}
int main(){
int t=1;
while(cin>>n&&n){
ys.clear();
for(int i=0,j=0;i<n;i++){
double x1,x2,y1,y2;
cin>>x1>>y1>>x2>>y2;
seg[j++]={x1,y1,y2,1};
seg[j++]={x2,y1,y2,-1};
ys.push_back(y1);
ys.push_back(y2);
}
sort(ys.begin(),ys.end());
ys.erase(unique(ys.begin(),ys.end()),ys.end());
build(1,0,ys.size()-2);
sort(seg,seg+2*n);
double res=0;
//根节点的长度即为此时有效线段长度
//乘此线段x轴和下一线段x轴的坐标为面积
for(int i=0;i<2*n;i++){
if(i>0) res+=tr[1].len*(seg[i].x-seg[i-1].x);
modify(1,find(seg[i].y1),find(seg[i].y2)-1,seg[i].k);
}
printf("Test case #%d\n", t++);
printf("Total explored area: %.2lf\n\n", res);
}
}
环形数组(区间修改,区间查询)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=2e5+5;
int n,m;
int a[N];
struct node{
int l,r;
ll lazy;
ll w;
}tr[N<<2];
void pushup(int u){
tr[u].w=min(tr[u<<1].w,tr[u<<1|1].w);
}
void pushdown(int u){
tr[u<<1].lazy+=tr[u].lazy;
tr[u<<1].w+=tr[u].lazy;
tr[u<<1|1].lazy+=tr[u].lazy;
tr[u<<1|1].w+=tr[u].lazy;
tr[u].lazy=0;
}
void build(int u,int l,int r){
if(l==r) tr[u]={l,r,0,a[l]};
else{
tr[u]={l,r,0};
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
}
void modify(int u,int l,int r,int d){
if(tr[u].l>=l&&tr[u].r<=r){
tr[u].w+=d;
tr[u].lazy+=d;
return;
}
int mid=tr[u].l+tr[u].r>>1;
pushdown(u);
if(l<=mid) modify(u<<1,l,r,d);
if(r>mid) modify(u<<1|1,l,r,d);
pushup(u);
}
ll query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r) return tr[u].w;
int mid=tr[u].l+tr[u].r>>1;
pushdown(u);
ll res=1e18;
if(l<=mid) res=min(res,query(u<<1,l,r));
if(r>mid) res=min(res,query(u<<1|1,l,r));
return res;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
build(1,1,n);
cin>>m;
getchar();
while(m--){
stringstream ss;string s;
getline(cin,s);
ss<<s;
int t,c=0,op[5];
while(ss>>t){
op[++c]=t;
}
int l=op[1]+1,r=op[2]+1,d=op[3];
if(c==2){
if(l<=r){
cout<<query(1,l,r)<<endl;
}else{
cout<<min(query(1,l,n),query(1,1,r))<<endl;
}
}else{
if(l<=r){
modify(1,l,r,d);
}else{
modify(1,l,n,d);
modify(1,1,r,d);
}
}
}
}