题目链接
题目背景
很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。这让很多学生很反感。
题目描述
不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩
输入输出格式
输入格式:
第一行,有两个正整数 N 和 M ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目。学生ID编号分别从1编到N。第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩。接下来有M行。每一行有一个字符 C (只取'Q'或'U') ,和两个正整数A,B。当C为'Q'的时候,表示这是一条询问操作,它询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。当C为'U'的时候,表示这是一条更新操作,如果当前A学生的成绩低于B,则把ID为A的学生的成绩更改为B,否则不改动。
输出格式:
对于每一次询问操作,在一行里面输出最高成绩
题解
根据题目要求我们可以发现,这道题要求我们维护一个数据结构,来支持单点修改和区间查询最值,于是我想到用线段树来实现。
我的线段树和其他题解中不太一样,是动态开点
对于题目中的要求
如果当前A学生的成绩低于B,则把ID为A的学生的成绩更改为B,否则不改动。
我们可以考虑在单点修改操作中用以下方式维护
1 void Insert(int &now,int l,int r,int x,int k){ 2 if(now==0) 3 now=++cnt; 4 if(l==r){ 5 Seg[now].sum=max(Seg[now].sum,k);//按照题目要求维护线段树 6 return; 7 } 8 int mid=(l+r)>>1; 9 if(x<=mid) 10 Insert(Seg[now].L,l,mid,x,k); 11 else 12 Insert(Seg[now].R,mid+1,r,x,k); 13 Seg[now].sum=max(Seg[Seg[now].L].sum,Seg[Seg[now].R].sum); 14 //维护线段树,以使每个节点都等于它子节点的最大值 15 }
对于查询操作,我们可以考虑这样维护
int Query(int now,int l,int r,int x,int y){ if(x<=l && r<=y) return Seg[now].sum; //如果要查询的区间比子节点代表的区间还小,直接返回子节点区间 int mid=(l+r)>>1; int maxL=0,maxR=0; if(x<=mid) maxL=max(maxL,Query(Seg[now].L,l,mid,x,y));//维护查询最大值 if(y>mid) maxR=max(maxR,Query(Seg[now].R,mid+1,r,x,y));//维护查询最大值 return max(maxL,maxR);//维护查询最大值 }
以下是完整代码
#include<iostream>//I Hate It!!!! #include<cstdio> using namespace std; struct Tree{ int L; int R; int sum; }Seg[800010]; int n,m,cnt,root; char C; void Build(int &now,int l,int r,int x,int k){//使用取址符来实现动态开点 if(now==0) now=++cnt;//动态开点的核心操作 if(l==r){ Seg[now].sum=k; return; } int mid=(l+r)>>1; if(x<=mid) Build(Seg[now].L,l,mid,x,k); else Build(Seg[now].R,mid+1,r,x,k); Seg[now].sum=max(Seg[Seg[now].L].sum,Seg[Seg[now].R].sum); //建树操作中也应维护查询最大值操作 } void Insert(int &now,int l,int r,int x,int k){ if(now==0) now=++cnt; if(l==r){ Seg[now].sum=max(Seg[now].sum,k); return; } int mid=(l+r)>>1; if(x<=mid) Insert(Seg[now].L,l,mid,x,k); else Insert(Seg[now].R,mid+1,r,x,k); Seg[now].sum=max(Seg[Seg[now].L].sum,Seg[Seg[now].R].sum); } int Query(int now,int l,int r,int x,int y){ if(x<=l && r<=y) return Seg[now].sum; int mid=(l+r)>>1; int maxL=0,maxR=0; if(x<=mid) maxL=max(maxL,Query(Seg[now].L,l,mid,x,y)); if(y>mid) maxR=max(maxR,Query(Seg[now].R,mid+1,r,x,y)); return max(maxL,maxR); } int main(){ //freopen("IHate.in","r",stdin); //freopen("IHate.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ register int k; scanf("%d",&k); Build(root,1,n,i,k); } for(int i=1;i<=m;i++){ cin>>C; if(C=='Q'){ register int x,y; scanf("%d%d",&x,&y); printf("%d\n",Query(root,1,n,x,y)); } else{ register int x,k; scanf("%d%d",&x,&k); Insert(root,1,n,x,k); } } return 0; }
感觉动态开点非常好理解为什么没人用呢
友情链接:安利一只小姐姐的博客