[kuangbin带你飞]专题七 线段树 题解(未完)

来源:互联网 发布:中国历届人口普查数据 编辑:程序博客网 时间:2024/06/05 09:49

[kuangbin带你飞]专题七 线段树

题解:
A 经典的单点更新,区间求和:

#include<bits/stdc++.h>using namespace std;#define root 1,n,1#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1const int N=5e4+10;int n,sum[N<<2];void push_up(int rt){   sum[rt]=sum[rt<<1]+sum[rt<<1|1];}void build(int l,int r,int rt){   if(l==r){      scanf("%d",&sum[rt]);      return;   }   int m=l+r >>1;   build(lson); build(rson);   push_up(rt);}void update(int o,int v,int l,int r,int rt){   if(l==r){     sum[rt]+=v;     return;   }   int m=l+r >>1;   if(o<=m) update(o,v,lson);   else update(o,v,rson);   push_up(rt);}int query(int L,int R,int l,int r,int rt){   if(L<=l&&R>=r) return sum[rt];   int m=l+r>>1;   int res=0;   if(L<=m) res+=query(L,R,lson);   if(R>m) res+=query(L,R,rson);   return res;}int main(){    int T; scanf("%d",&T);    int cas = 0;    while(T--) {        printf("Case %d:\n", ++cas);        scanf("%d",&n);        build(root);        char op[10]; int x, y;        while(scanf("%s",op)) {            if(op[0] == 'E') break;            scanf("%d%d",&x,&y);            if(op[0] == 'Q') printf("%d\n",query(x, y,root));            else if(op[0] == 'A') update(x, y, root);            else if(op[0] == 'S') update(x, -y, root);        }    }}

B.经典的单点更新,区间查询最大值

#include<bits/stdc++.h>using namespace std;const int N=2e5+10,inf=0x37373737;int maxx[N*4];void push_up(int x){   maxx[x]=max(maxx[x<<1],maxx[x<<1|1]);}void build(int x,int l,int r){  if(l==r){     scanf("%d",&maxx[x]);     return;  }  int mid=(l+r)>>1;  build(x<<1,l,mid);  build(x<<1|1,mid+1,r);  push_up(x);}void update(int o,int v,int l,int r,int x){  if(l==r){     maxx[x]=v;     return;  }  int mid=(l+r)>>1;  if(mid>=o) update(o,v,l,mid,x<<1);  else update(o,v,mid+1,r,x<<1|1);  push_up(x);}int query(int L,int R,int l,int r,int x){  if(L<=l&&R>=r) return maxx[x];  int res=-inf;  int mid=(l+r)>>1;  if(L<=mid) res=max(res,query(L,R,l,mid,x<<1));  if(R>mid) res=max(res,query(L,R,mid+1,r,x<<1|1));  return res;}int main(){   int n,m;   while(~scanf("%d%d",&n,&m)){     build(1,1,n);     while(m--){        char op[2];scanf("%s",op);        int a,b;scanf("%d%d",&a,&b);        if(op[0]=='Q') printf("%d\n",query(a,b,1,n,1));        else update(a,b,1,n,1);     }   }}

C.区间更新,区间查询
区间更新加了一个延迟更新的push_down操作,就是在更新或者查询到当前节点的左右儿子节点时,提前更新它的左右儿子节点,保证要查询当前区间才会去更新他下面的节点。
这就是懒惰操作lazy propagation 。

#include<cstdio>#include<cstring>#include<iostream>using namespace std;typedef long long ll;#define root 1, n, 1#define lson l, m, rt << 1#define rson m + 1, r, rt << 1 | 1const int N=1e5+6;ll sum[N<<4],add[N<<4];int n,q;void push_up(int rt){   sum[rt]=sum[rt<<1]+sum[rt<<1|1];}void push_down(int rt,int m){    if(add[rt]){        add[rt<<1]+=add[rt];        add[rt<<1|1]+=add[rt];        sum[rt<<1]+=add[rt]*(m-(m>>1));        sum[rt<<1|1]+=add[rt]*(m>>1);        add[rt]=0;    }}void build(int l,int r,int rt){   add[rt]=0;   if(l==r){      scanf("%lld",&sum[rt]);      return;   }   int m=(l+r)>>1;   build(lson);   build(rson);   push_up(rt);}void update(int L,int R,int v,int l,int r,int rt){   if(L<=l&&r<=R){      add[rt]+=v;      sum[rt]+=(ll)v*(r-l+1);      return;   }   push_down(rt,r-l+1);   int m=l+r >>1;   if(L<=m) update(L,R,v,lson);   if(R>m) update(L,R,v,rson);   push_up(rt);}ll query(int L,int R,int l,int r,int rt){    if(L<=l&&r<=R) return sum[rt];    push_down(rt,r-l+1);    int m=(l+r)>>1;    ll res=0;    if(L<=m) res+=query(L,R,lson);    if(R>m) res+=query(L,R,rson);    return res;}int main(){    while(scanf("%d%d",&n,&q)==2){        build(root);        while(q--){            char op[2]; int x, y, z;            scanf("%s%d%d", op, &x, &y);            if(op[0] == 'Q') printf("%lld\n", query(x,y,root));            else {                scanf("%d",&z);                update(x,y,z, root);            }        }    }}

D区间点染色问题.
先离散化处理点范围,区间染色问题实际就是
详细题解

#include<cstdio>#include<cstring>#include<algorithm>#include<vector>#include<iostream>#include<queue>using namespace std;const int N=1e4+10;int x1[N],x2[N],vis[6*N*4],cnt[N],ans,n;int compress(int *x1,int *x2,int w){   vector<int> xs;   for(int i=1;i<=n;i++){     for(int d=-1;d<=1;d++){        int tx1=x1[i]+d,tx2=x2[i]+d;        if(1<=tx1&&tx1<=w) xs.push_back(tx1);        if(1<=tx2&&tx2<=w) xs.push_back(tx2);     }   }   sort(xs.begin(),xs.end());   xs.erase(unique(xs.begin(),xs.end()),xs.end());   for(int i=1;i<=n;i++){     x1[i]=find(xs.begin(),xs.end(),x1[i])-xs.begin()+1;     x2[i]=find(xs.begin(),xs.end(),x2[i])-xs.begin()+1;   }   return xs.size();}void push_down(int rt){   if(vis[rt]){      vis[rt<<1]=vis[rt<<1|1]=vis[rt];      vis[rt]=0;   }}void update(int L,int R,int c,int l,int r,int rt){   if(L<=l&&r<=R){      vis[rt]=c;      return;   }   int m=(l+r)>>1;   push_down(rt);   if(L<=m) update(L,R,c,l,m,rt<<1);   if(R>m) update(L,R,c,m+1,r,rt<<1|1);}void query(int l,int r,int rt){   if(vis[rt]){      if(!cnt[vis[rt]]){          ans++;cnt[vis[rt]]=1;      }      return;   }   if(l==r) return;   int m=l+r>>1;   query(l,m,rt<<1);   query(m+1,r,rt<<1|1);}int main(){  int T;scanf("%d",&T);  while(T--){     int w=0;scanf("%d",&n);     for(int i=1;i<=n;i++){        scanf("%d%d",x1+i,x2+i);     }     w=compress(x1,x2,1e7);     //cout<<"ok"<<w<<endl;     memset(cnt,0,sizeof(cnt));     memset(vis,0,sizeof(vis));     for(int i=1;i<=n;i++){        update(x1[i],x2[i],i,1,w,1);     }     ans=0;     //cout<<"ok"<<endl;     query(1,w,1);     printf("%d\n",ans);  }}

E区间修改,区间求和

#include<bits/stdc++.h>using namespace std;typedef long long ll;#define root 1, n, 1#define lson l, m, rt << 1#define rson m + 1, r, rt << 1 | 1const int N=1e5+6;ll sum[N<<4],add[N<<4];int n,q;void push_up(int rt){   sum[rt]=sum[rt<<1]+sum[rt<<1|1];}void push_down(int rt,int m){    if(add[rt]){        add[rt<<1]=add[rt];        add[rt<<1|1]=add[rt];        sum[rt<<1]=add[rt]*(m-(m>>1));        sum[rt<<1|1]=add[rt]*(m>>1);        add[rt]=0;    }}void build(int l,int r,int rt){   add[rt]=0;   if(l==r){      sum[rt]=1;      return;   }   int m=(l+r)>>1;   build(lson);   build(rson);   push_up(rt);}void update(int L,int R,int v,int l,int r,int rt){   if(L<=l&&r<=R){      add[rt]=v;      sum[rt]=(ll)v*(r-l+1);      return;   }   push_down(rt,r-l+1);   int m=l+r >>1;   if(L<=m) update(L,R,v,lson);   if(R>m) update(L,R,v,rson);   push_up(rt);}ll query(int L,int R,int l,int r,int rt){    if(L<=l&&r<=R) return sum[rt];    push_down(rt,r-l+1);    int m=(l+r)>>1;    ll res=0;    if(L<=m) res+=query(L,R,lson);    if(R>m) res+=query(L,R,rson);    return res;}int main(){    int T,cnt=1;scanf("%d",&T);    while(T--){        scanf("%d%d",&n,&q);        build(root);        while(q--){            int a,b,c;scanf("%d%d%d",&a,&b,&c);            update(a,b,c,root);        }        printf("Case %d: The total value of the hook is %lld.\n",cnt++,query(1,n,root));    }}

F 区间线段染色问题.

#include<bits/stdc++.h>using namespace std;const int N=1e4;int vis[N<<2],col[N],last;void push_down(int rt){   if(vis[rt]>=0){      vis[rt<<1]=vis[rt<<1|1]=vis[rt];      vis[rt]=-1;   }}void update(int L,int R,int c,int l,int r,int rt){   if(L<=l&&r<=R){      vis[rt]=c;      return;   }   int m=(l+r)>>1;   push_down(rt);   if(L<=m) update(L,R,c,l,m,rt<<1);   if(R>m) update(L,R,c,m+1,r,rt<<1|1);}void query(int l,int r,int rt){   if(l==r){      if(vis[rt]!=last) col[vis[rt]]++;      last=vis[rt];      return;   }   push_down(rt);   int m=l+r>>1;   query(l,m,rt<<1);   query(m+1,r,rt<<1|1);}int main(){    int n;    while(~scanf("%d",&n)){        memset(vis,-1,sizeof(vis));        memset(col,0,sizeof(col));        for(int i=0;i<n;i++){            int a,b,c;scanf("%d%d%d",&a,&b,&c);            if(a+1<=b) update(a+1,b,c,1,8000,1);        }        last=-1;        query(1,8000,1);        for(int i=0;i<=8000;i++){            if(col[i]>0) printf("%d %d\n",i,col[i]);        }        printf("\n");    }}

G 没有修改的区间最值问题

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int N=1<<16;int dmin[N][16],dmax[N][16],n,m,A[N];void RMQ_init(){   for(int i=0;i<n;i++) dmin[i][0]=A[i],dmax[i][0]=A[i];   for(int j=1;(1<<j)<=n;j++){     for(int i=0;i+(1<<j)-1<n;i++){        dmin[i][j]=min(dmin[i][j-1],dmin[i+(1<<(j-1))][j-1]);        dmax[i][j]=max(dmax[i][j-1],dmax[i+(1<<(j-1))][j-1]);     }   }}int rmq(int l,int r,int ok){//ok=0返回最小值,ok=1返回最大值   int k=0;   while((1<<(k+1))<=r-l+1) k++;   return ok==0 ? min(dmin[l][k],dmin[r-(1<<k)+1][k]) : max(dmax[l][k],dmax[r-(1<<k)+1][k]);}int main(){    while(~scanf("%d%d",&n,&m)){        for(int i=0;i<n;i++) scanf("%d",&A[i]);        RMQ_init();        while(m--){            int a,b;scanf("%d%d",&a,&b);            printf("%d\n",rmq(a-1,b-1,1)-rmq(a-1,b-1,0));        }    }}

H.区间开方的修改,区间求和
看似无法懒惰标记的区间求和问题。但题目里隐含了只要求和求整数和,根据题目所有数之和不超过2^63我们可以估计区间和不会更新很多次就变成1。区间更新时候有一个剪枝,不需要push_down操作。

#include<bits/stdc++.h>using namespace std;typedef long long ll;const int N=1e5+10;#define root 1,n,1#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1ll sum[N<<2];void push_up(int rt){    sum[rt]=sum[rt<<1]+sum[rt<<1|1];}void build(int l,int r,int rt){    if(l==r){       scanf("%lld",&sum[rt]);       return;    }    int m=(l+r)>>1;    build(l,m,rt<<1);    build(m+1,r,rt<<1|1);    push_up(rt);}void update(int L,int R,int l,int r,int rt){   if(sum[rt]==r-l+1) return;   if(l==r){      sum[rt]=sqrt(sum[rt]);      return;   }   int m=(l+r)>>1;   if(L<=m) update(L,R,lson);   if(R>m) update(L,R,rson);   push_up(rt);}ll query(int L,int R,int l,int r,int rt){   if(L<=l&&r<=R){      return sum[rt];   }   ll res=0;   int m=(l+r)>>1;   if(L<=m)  res+=query(L,R,lson);   if(R>m) res+=query(L,R,rson);   return res;}int main(){   int n,cas=1;   while(scanf("%d",&n)==1){        build(root);        int q;scanf("%d",&q);        printf("Case #%d:\n",cas++);        while(q--){            int o,a,b;scanf("%d%d%d",&o,&a,&b);            if(a>b) swap(a,b);            if(o==0) update(a,b,1,n,1);            else printf("%lld\n",query(a,b,1,n,1));        }        printf("\n");   }}

I求连续区间最大和
题解

#include<bits/stdc++.h>using namespace std;#define root 1,n,1#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1const int N=5e4+10;int lsum[N<<2],rsum[N<<2],sta[N],n,q;void push_up(int rt,int m){   lsum[rt]=lsum[rt<<1];   rsum[rt]=rsum[rt<<1|1];   if(lsum[rt<<1]==m-(m>>1)) lsum[rt]+=lsum[rt<<1|1];   if(rsum[rt<<1|1]==m>>1) rsum[rt]+=rsum[rt<<1];}void build(int l,int r,int rt){   lsum[rt]=rsum[rt]=r-l+1;   if(l==r) return;   int m=l+r >>1;   build(l,m,rt<<1);   build(m+1,r,rt<<1|1);}void update(int o,int v,int l,int r,int rt){   if(l==r){       lsum[rt]=rsum[rt]=v;       return;   }   int m=l+r>>1;   if(o<=m) update(o,v,lson);   else update(o,v,rson);   push_up(rt,r-l+1);}int query(int o,int l,int r,int rt){    if(l==r) return 0;    int m=l+r >>1;    if(o>=m-rsum[rt<<1]+1&&o<=m+lsum[rt<<1|1]){        return rsum[rt<<1]+lsum[rt<<1|1];    }    if(o<=m) return query(o,lson);    else return query(o,rson);}int main(){    while(~scanf("%d%d",&n,&q)){        build(root);        int t=0;        while(q--){            char op[2];scanf("%s",op);            if(op[0]=='R'){                int x=sta[--t];                update(x,1,root);            }            else{                int x;scanf("%d",&x);                if(op[0]=='D'){                    update(x,0,root);                    sta[t++]=x;                }                else printf("%d\n",query(x,root));            }        }    }}

J.dfs序+区间染色染色
题意:给定一个树(n<=50000),有两个操作(q<=50000):
1.更新一个节点信息,他的子孙节点都会被更新成同样信息.
2.查询任意一个节点的信息。

思路:
从树的根开始一遍dfs序,用l,r两个数组构造重新给每个节点分配的范围,比如根节点就是[1,n],一直到叶子节点。
然后就是区间点染色问题。

#include<bits/stdc++.h>using namespace std;const int N=5e4+10;int l[N],r[N],vis[N<<2];#define root 1,n,1#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1void push_down(int rt){   if(vis[rt]>=0){      vis[rt<<1]=vis[rt<<1|1]=vis[rt];      vis[rt]=-1;   }}void update(int L,int R,int c,int l,int r,int rt){   if(L<=l&&r<=R){      vis[rt]=c;      return;   }   int m=(l+r)>>1;   push_down(rt);   if(L<=m) update(L,R,c,l,m,rt<<1);   if(R>m) update(L,R,c,m+1,r,rt<<1|1);}int query(int o,int l,int r,int rt){   if(l==r){      return vis[rt];   }   push_down(rt);   int m=l+r>>1;   if(o<=m)return query(o,lson);   else return query(o,rson);}vector<int>E[N];int cnt;bool use[N];void dfs(int u){    l[u]=++cnt;    for(int i=0;i<E[u].size();i++){        int v=E[u][i];        dfs(v);    }    r[u]=cnt;}int main(){    int T,cas=1;scanf("%d",&T);    while(T--){        cnt=0;        int n;scanf("%d",&n);        for(int i=0;i<=n;i++) E[i].clear();        memset(use,false,sizeof(use));        for(int i=0;i<n-1;i++){           int a,b;scanf("%d%d",&a,&b);           E[b].push_back(a);           use[a]=true;        }        for(int i=1;i<=n;i++){            if(!use[i]){                dfs(i);                break;            }        }        printf("Case #%d:\n",cas++);        memset(vis,-1,sizeof(vis));        int q;scanf("%d",&q);        while(q--){            char op[2];scanf("%s",op);            if(op[0]=='C'){                int x;scanf("%d",&x);                printf("%d\n",query(l[x],1,cnt,1));            }            else{               int a,b;scanf("%d%d",&a,&b);               update(l[a],r[a],b,1,cnt,1);            }        }    }}

“`

1 0
原创粉丝点击