1. 程式人生 > 實用技巧 >[SCOI2007]降雨量 線段樹和區間最值(RMQ)問題

[SCOI2007]降雨量 線段樹和區間最值(RMQ)問題

  這道題是比較經典的 \(RMQ\) 問題,用線段樹維護是比較簡單好寫的。比較難的部分是判斷處理。如果沒有想好直接打程式碼會調很久(沒錯就是我)。怎麼維護查詢區間最大值我就不再這裡贅述了,不懂線段樹的先去入門(此題也是線段樹入門題)。我講幾個很坑的點(比較坑我的點):
  1.詢問的X年降雨量不超過Y,但是中間年份降雨量一定小於X(注意X和Y順序)。
  2.X可能等於Y+1年,也就是不用考慮中間年份。
  3.區間查詢最值的操作要留意範圍,不同情況下查詢的範圍是不一樣的,這點需要自己理解。
  4.錯的最多的 \(maybe\)\(false\) 的判斷,要清楚知道哪個是已知量,哪個是位置量。
  本題中我分了四種情況分析,還有眾多 \(if\)

\(else\) ,需要比較小心。我對輸出進行簡化,應該會更加直觀一點。

\(Code\)

#include<bits/stdc++.h>
using namespace std;
#define For(i,sta,en) for(int i = sta;i <= en;i++)
#define lowbit(x) x&(-x)
#define mid (l+r)/2
#define ls(x) x<<1
#define rs(x) x<<1|1
#define speedUp_cin_cout ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);
typedef long long ll;
typedef __int128 lll;
const int maxn = 2e5+9;
int t[maxn],n,m;  //t 記錄區間最大值
int year[maxn],rain[maxn];

void update(int now,int l,int r,int pos,int value){
    if(l == r) {t[now] = value;return;}
    if(pos <= mid) update(ls(now),l,mid,pos,value);
    else update(rs(now),mid+1,r,pos,value);
    t[now] = max(t[ls(now)],t[rs(now)]);  //維護區間最大值
}

int query(int now,int l,int r,int x,int y){  //詢問x到y區間最大值
    if(x <= l&&r <= y) return t[now];
    int an = 0;
    if(x <= mid) an = query(ls(now),l,mid,x,y);
    if(y > mid) an = max(an,query(rs(now),mid+1,r,x,y));
    return an;
}

int main(){
    speedUp_cin_cout  //讀寫優化
    #define maybe cout<<"maybe"<<endl;continue;//讓輸出沒這麼難看
    #define false cout<<"false"<<endl;continue;
    #define true cout<<"true"<<endl;continue;
    cin>>n;
    int y,r;
    For(i,1,n)  {
        cin>>y>>r;
        year[i] = y;
        rain[i] = r;
        update(1,1,n,i,r);
    }
    cin>>m;
    int Y,X,p1,p2,f1,f2; //p1 p2是在陣列的位置,f1 f2兩個標記記錄X Y是否已知降水量
    For(i,1,m){
        cin >> Y >> X;
        p1 = lower_bound(year+1,year+1+n, Y) - year; //year在輸入時保證是有序的
        p2 = lower_bound(year+1,year+1+n, X) - year;
        f1 = (p1 == n + 1|| year[p1] != Y) ? 0 : 1;
        f2 = (p2 == n + 1 ||year[p2] != X) ? 0 : 1;
        //兩年都不知道,可以不管中間如何,都是未知的
        if(!f1 && !f2) {maybe}
        //兩年都知道
        if(f1 && f2) {
            if(rain[p1] < rain[p2]) {false}   //X年降水量多於Y年,錯
            if(Y + 1 == X ) {true}          //X = Y+1年,中間沒有其他年份,而X年降水量一定不大於Y,對
            if(p1 + 1 == p2) {maybe}       //X不是Y後一年,但是X和Y年間都不知道降雨量,未知
            int maxGap = query(1,1,n,p1+1,p2-1);   //X和Y間存在已知降水量的年份,在其中找到降雨量最大值
            if(maxGap >= rain[p2]) {false}   //大於等於都滿足嚴格小於,錯
            if(p2 - p1 == X - Y) {true}                 //滿足嚴格小於後再判斷X和Y間每年降雨量是否都已知
            else maybe                                      //不是都已知,即未知
        }
        //後一年不知道,p2 一定大於 p1,只可能是maybe或者false
        else if(f1){
            if( p1 + 1 == p2 ) {maybe}
            int maxGap = query(1,1,n,p1+1,p2-1);  //注意查詢範圍
            if(maxGap >= rain[p1]) { false }
            else maybe
        }
        //前一年不知道,p1 有可能等於 p2,只可能是maybe或者false
        else{
            if(p1  == p2 ) {maybe}
            int maxGap = query(1,1,n,p1,p2-1);  //注意查詢範圍
            if(maxGap >= rain[p2]) { false }
            else { maybe }
        }
    }
    return 0;
}