[是题解哦] 洛谷 P1531 I Hate It

题目链接

点这里点这里

题目背景

很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。这让很多学生很反感。

题目描述

不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩

输入输出格式

输入格式:

第一行,有两个正整数 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;
}

感觉动态开点非常好理解为什么没人用呢

友情链接:安利一只小姐姐的博客

猜你喜欢

转载自www.cnblogs.com/tatarakogasa/p/9782087.html