hdu-1754 I Hate It【线段树】(求区间最大值)

题目链接:https://vjudge.net/contest/182746#problem/A

                                         I Hate It

                               Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
                                                    Total Submission(s): 96252    Accepted Submission(s): 36407

Problem Description
很多学校流行一种比较的习惯。老师们很喜欢询问,从某某到某某当中,分数最高的是多少。
这让很多学生很反感。
不管你喜不喜欢,现在需要你做的是,就是按照老师的要求,写一个程序,模拟老师的询问。当然,老师有时候需要更新某位同学的成绩。
Input
本题目包含多组测试,请处理到文件结束。
在每个测试的第一行,有两个正整数 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'的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。
Output
对于每一次询问操作,在一行里面输出最高成绩。
Sample Input
5 6
1 2 3 4 5
Q 1 5
U 3 6
Q 3 4
Q 4 5
U 2 9
Q 1 5
Sample Output
5
6
5
9
 
解题分析:
典型的线段树问题,由于数据比较大,所以用查询答案和修改数据都非常快的线段树。      不熟悉线段树的朋友可以看看这篇博客        >>>
以下附代码,以及初学者一些浅薄的理解
 
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 2000000;
int n, m, a[MAXN + 5];

struct tree
{
    int l, r;           //每个节点代表一个区间
    int v;              //v表示这个区间内的最大值
}trees[MAXN * 2];       //用数组来实现线段树,2*i代表第i个节点的左儿子,2*i+1代表第i个节点的右儿子

int max(int a, int b){    return a > b ? a : b;}

void buildtree(int rs, int l, int r)   //先从根节点将该线段树建好
{
    trees[rs].l = l;
    trees[rs].r = r;
    if (r == l)
    {
        trees[rs].v = a[l];     //当构建到叶子的时候,该叶子的值为每一个元素对应的值
        return;
    }
    int mid = (l + r) / 2;      //将该区间均分成两部分
    buildtree(rs * 2, l, mid);
    buildtree(rs * 2 + 1, mid + 1, r);
    trees[rs].v = max(trees[rs * 2].v, trees[rs * 2 + 1].v);   //利用递归,一层一层自下而上给每一个节点赋值
}

void update(int rs, int k, int l)
{
    if (trees[rs].l == k && trees[rs].r == k)         //如果找到序号为k的叶子,则将该叶子的值更新为l
    {
        trees[rs].v = l;
        return;
    }
    int mid = (trees[rs].l + trees[rs].r) / 2;       //选择包含序号为k的叶子的路径进行更新,减少不必要的操作
    if (k <= mid)update(rs * 2, k, l);
    if (k>mid)update(rs * 2 + 1, k, l);
    trees[rs].v = max(trees[rs * 2].v, trees[rs * 2 + 1].v);    //利用递归,自下而上的进行更新
}

int querry(int rs, int l, int r)
{
    if (trees[rs].l == l && trees[rs].r == r)         //如果已经找到需要查询的区间,那么直接返回符合该区间的节点对应的值
        return trees[rs].v;
    int mid = (trees[rs].l + trees[rs].r) / 2;
    if (r <= mid)return querry(rs * 2, l, r);         //如果需要查询的区间完全包含在该节点的左儿子内,则顺着该节点的左儿子继续查找
    if (l>mid)return querry(rs * 2 + 1, l, r);
    if (l <= mid && r>mid)
        return max(querry(rs * 2, l, mid), querry(rs * 2 + 1, mid + 1, r));     //如果需要查询的区间一部分在左儿子的区间内,另一部分在右儿子的区间内,则该区间的最大值就在[l,mid]或者[mid+1,r]中取得,比较一下两个区间的最大值即可
    return 0;          //如果不符合条件,则返回0
}

int main()
{
    int i, ac, bc;
    char c;
    while (scanf("%d %d", &n, &m) != EOF)
    {
        for (i = 1; i <= n; i++)
            scanf("%d", &a[i]);
        buildtree(1, 1, n);          //从根节点将线段树建好
        for (i = 1; i <= m; i++)
        {
            getchar();
            scanf("%c%d%d", &c, &ac, &bc);
            if (c == 'Q')
                printf("%d\n", querry(1, ac, bc));      //ac,bc分别为将要查询的区间边界
            else
                update(1, ac, bc);                      //ac表示学生的序号,bc表示该学生更改后的成绩
        }
    }
    return 0;
}
 
2018-07-19

猜你喜欢

转载自www.cnblogs.com/00isok/p/9339152.html