1. 程式人生 > >P1531 I Hate It(史上最好懂的線段樹之一)

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,否則不改動。

 

輸出格式:

 

對於每一次詢問操作,在一行裡面輸出最高成績

 

輸入輸出樣例

輸入樣例#1: 複製

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

輸出樣例#1: 複製

5
6
5
9

這個題超級超級超級裸的線段樹(線段樹是個好東西)

因為不會用樹狀陣列,所以本蒟蒻來一手線段樹(大佬勿噴)

史上最好懂的線段樹之一

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
long long aa[500001],k,n,m;
long long init(int n_)
{
    n=1;//為了方便,將葉節點數擴大到2的冪
    while(n<=n_){n=n*2;}
}
long long add(int k,int x)
{
    k=k+n-1;
    aa[k]=x;//將第k個葉節點修改成x
    while(k>0)
    {
        k=k/2;//向上修改最大值
        aa[k]=max(aa[k*2],aa[k*2+1]);
    }
}
long long check(int a,int b,int k,int l,int r)//a,b是所求區間範圍,k是當前節點編號,l,r是第k號節點的所管區間
{
    //注意:根節點為1
    if(r<a||l>b)
        return 0;//如果該節點所管區間與要求區間不重合,返回0
    if(a<=l&&r<=b)
        return aa[k];//如果該節點所管區間完全包含於所求區間,返回該節點的值。
    else
    {
        long long maxl=check(a,b,k*2,l,(l+r)/2);//左孩子
        long long maxr=check(a,b,k*2+1,(l+r)/2+1,r);//右孩子
        return max(maxl,maxr);//返回兩個孩子的最大值
    }
}
int main()
{
    cin>>n>>m;
    int nn=n;
    init(n);//初始化
    for(int i=1;i<=nn;i++)
    {
        int x;
        cin>>x;
        add(i,x);
    }    
    for(int i=0;i<m;i++)
    {
        char b;
        cin>>b;
        int l,r;
        cin>>l>>r;
        if(b=='U')//修改
        {
            if(aa[l+n-1]<r)
                add(l,r);
        }
        if(b=='Q')//查詢
            cout<<check(l,r,1,1,n)<<endl;//要從根節點開始搜尋
    }
    return 0;
}