1. 程式人生 > 實用技巧 >Luogu P1531 I Hate It

Luogu P1531 I Hate It

思路

這道題其實並不難,充其量就是一道線段樹(單點修改+區間維護最大值)的板子。但是基於嚴謹務實的態度,我們還是應該認真看一下這個道題。

線段樹的基本寫法及原理在這裡不再贅述,下面我們來說一下單點修改操作。

這道題的單點修改操作跟線段樹板子的單點修改不太一樣,這道題的單點修改不是單純地將一個點的值改為另一個值,而是對當前點的原本的值與給定的值取一個最大值(其實也沒啥區別,就是在update

的時候把替換操作改為對兩個值取max即可)。單點修改就這些,其實就是把線段樹板子稍稍改動了一下。

區間查詢操作也很簡單,說白了就是把區間求和的操作改成區間求最大值即可。具體操作就是把push_up、build、update和query操作中的求和改為取max即可。具體細節還是看程式碼吧。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define INF 0x7fffffff
#define MAXN 2000010
typedef long long ll;
//以上應該沒有疑問
ll n, m, a[MAXN];
ll tree[MAXN<<2];
inline ll read(void){//快讀,其實我也沒用到
    ll x = 0, f = 1;char ch;
    do{ch = getchar();if (ch == '-' )f = -1;}while(ch < '0' || ch > '9');
    do{x = x * 10 + ch - '0';ch = getchar();}while (ch >= '0' && ch <= '9');
    return f * x;
}
//求左兒子
inline ll lson(ll k) { return k << 1; }
//求右兒子
inline ll rson(ll k) { return k << 1 | 1; }
//上傳操作,注意是求max,不是求和
inline void push_up(ll k){
    tree[k] = std::max(tree[lson(k)], tree[rson(k)]);
    return;
}
//建樹(主程式裡一定不要忘了執行啊!)
void build(ll l,ll r,ll k){
    if(l==r){//若遍歷到葉子結點,直接賦值
        tree[k] = a[l];
        return;
    }
    //否則分別遍歷左右子樹
    ll mid = (l + r) >> 1;
    //遍歷左子樹
    build(l, mid, lson(k));
    //遍歷右子樹
    build(mid+1,r,rson(k));
    //別忘了上傳
    push_up(k);
    return;
}
void update(ll x,ll l,ll r,ll k,ll v){
    if(l==r&&l==x){//若當前節點為需要更改的點
        tree[k] = std::max(tree[k], v);//更改取max
        return;
    }
    ll mid = (l + r) >> 1;
    //若有一部分在左子樹,向左子樹遍歷
    if(x<=mid)update(x,l,mid,lson(k),v);
    //若有一部分在右子樹,向右子樹遍歷
    else update(x,mid+1,r,rson(k),v);
    //上傳
    push_up(k);
}
ll query(int ql,int qr,int l,int r,int k){
    ll res = -INF;//賦值成-INF比較保險
    if(ql<=l&&r<=qr)return tree[k];//若當前區間被查詢區間完全包括,直接返回
    ll mid = (l + r) >> 1;
    //若查詢區間一部分在左子樹,遍歷左子樹
    if(ql<=mid)res=std::max(res,query(ql,qr,l,mid,lson(k)));
    //若查詢區間一部分在右子樹,遍歷右子樹
    if(qr>mid)res=std::max(res,query(ql,qr,mid+1,r,rson(k)));
    return res;
}
int main(){
    scanf("%lld%lld",&n,&m);
    for (int i = 1; i <= n;++i)
        scanf("%lld",&a[i]);
    //讀入……
    build(1,n,1);//千萬別忘了建樹
    for (int i = 1; i <= m;++i){
        char opt[20];
        scanf("%s", opt);//讀入……
        if(opt[0]=='Q'){
            ll x = 0, y = 0;//查詢x~y區間內的最大值
            scanf("%lld%lld", &x, &y);
            printf("%lld\n", query(x, y, 1, n, 1));
        }
        if(opt[0]=='U'){
            ll x = 0, y = 0;
            scanf("%lld%lld", &x, &y);
            //將x位置的數與y取max
            update(x, 1, n, 1, y);
        }
    }
    return 0;
}