[kuangbin带你飞]专题七 线段树 题解(未完)
2017-04-05 23:32
567 查看
[kuangbin带你飞]专题七 线段树
题解:
A 经典的单点更新,区间求和:
B.经典的单点更新,区间查询最大值
C.区间更新,区间查询
区间更新加了一个延迟更新的push_down操作,就是在更新或者查询到当前节点的左右儿子节点时,提前更新它的左右儿子节点,保证要查询当前区间才会去更新他下面的节点。
这就是懒惰操作lazy propagation 。
D区间点染色问题.
先离散化处理点范围,区间染色问题实际就是
详细题解
E区间修改,区间求和
F 区间线段染色问题.
G 没有修改的区间最值问题
H.区间开方的修改,区间求和
看似无法懒惰标记的区间求和问题。但题目里隐含了只要求和求整数和,根据题目所有数之和不超过2^63我们可以估计区间和不会更新很多次就变成1。区间更新时候有一个剪枝,不需要push_down操作。
I求连续区间最大和
题解
J.dfs序+区间染色染色
题意:给定一个树(n<=50000),有两个操作(q<=50000):
1.更新一个节点信息,他的子孙节点都会被更新成同样信息.
2.查询任意一个节点的信息。
思路:
从树的根开始一遍dfs序,用l,r两个数组构造重新给每个节点分配的范围,比如根节点就是[1,n],一直到叶子节点。
然后就是区间点染色问题。
“`
题解:
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|1 const 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 | 1 const 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 ,x2 ,vis[6*N*4],cnt ,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 | 1 const 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 ,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 [16],dmax [16],n,m,A ; 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|1 ll 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|1 const int N=5e4+10; int lsum[N<<2],rsum[N<<2],sta ,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 ,r ,vis[N<<2]; #define root 1,n,1 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 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); } 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 ; int cnt; bool use ; 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); } } } }
“`
相关文章推荐
- [kuangbin带你飞]专题七 线段树 ABCDE 题解,持续更新
- [kuangbin带你飞]专题七 线段树 B - I Hate It(单点修改,区间最大值)
- [kuangbin带你飞]专题七 线段树 E - Just a Hook
- [kuangbin带你飞]专题五 并查集——题解
- 【算法系列学习】线段树 单点覆盖,区间查询最大值 [kuangbin带你飞]专题七 线段树 B - I Hate It
- 【算法系列学习】线段树 区间修改,区间求和 [kuangbin带你飞]专题七 线段树 C - A Simple Problem with Integers
- [kuangbin带你飞]专题七 线段树 I HDU 1540
- [kuangbin带你飞]专题九 连通图 题解报告
- [kuangbin带你飞]专题七 线段树
- [kuangbin带你飞]专题七 线段树 F - Count the Colors(颜色覆盖)
- [kuangbin带你飞]专题二 搜索进阶 题解(康托展开、映射、迭代加深)
- [kuangbin带你飞]专题七 线段树 B HDU 1754
- [kuangbin带你飞]专题七 线段树 G POJ 3264
- [kuangbin带你飞]专题七 线段树 C POJ 3468
- [kuangbin带你飞]专题七 线段树 J HDU 3974
- [kuangbin带你飞]专题七 线段树 A - 敌兵布阵 (单点修改,区间求和)
- [kuangbin带你飞]专题七 线段树 H HDU-4027
- [kuangbin带你飞]专题七 线段树 G POJ 3264
- [kuangbin带你飞]专题七 线段树 【A、B、C、E、G、H】
- 【算法系列学习】线段树vs树状数组 单点修改,区间查询 [kuangbin带你飞]专题七 线段树 A - 敌兵布阵