1. 程式人生 > >洛谷P1198 [JSOI2008]最大數(線段樹/單調棧)

洛谷P1198 [JSOI2008]最大數(線段樹/單調棧)

題目連結:

https://www.luogu.org/problemnew/show/P1198

題目描述

現在請求你維護一個數列,要求提供以下兩種操作:

1、 查詢操作。

語法:Q L

功能:查詢當前數列中末尾L個數中的最大的數,並輸出這個數的值。

限制:LL不超過當前數列的長度。(L > 0)(L>0)

2、 插入操作。

語法:A n

功能:將nn加上tt,其中tt是最近一次查詢操作的答案(如果還未執行過查詢操作,則t=0t=0),並將所得結果對一個固定的常數DD取模,將所得答案插入到數列的末尾。

限制:nn是整數(可能為負數)並且在長整範圍內。

注意:初始時數列是空的,沒有一個數。

輸入輸出格式

輸入格式:

 

第一行兩個整數,MM和DD,其中MM表示操作的個數(M \le 200,000)(M200,000),DD如上文中所述,滿足(0<D<2,000,000,000)(0<D<2,000,000,000)

接下來的MM行,每行一個字串,描述一個具體的操作。語法如上文所述。

 

輸出格式:

對於每一個查詢操作,你應該按照順序依次輸出結果,每個結果佔一行。

輸入輸出樣例

輸入樣例#1:  複製
5 100
A 96
Q 1
A 97
Q 1
Q 2
輸出樣例#1:  複製
96
93
96

單調棧解法。因為題目求的是末尾L個數的最大值,利用兩個棧分別儲存最大值和最大值的位置。然後二分查詢。注意二分查詢時上界是總得個數減去區間長度,而不是棧的空間減去區間長度,因為不符合棧的數值已經出棧了。

單調棧解法參考自:https://www.luogu.org/blog/user38348/solution-p1198

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long 
using namespace std; 
long long Stack[2][200010];
long long cnt=0,top=0;
void add(long long val){
    cnt++;
    while(Stack[0][top]<val&&(top>0
)) top--; Stack[0][++top]=val; Stack[1][top]=cnt; } long long quiry(long long L){ int l=1,r=top; int ind=cnt-L+1; // int ind=top-L+1; while(l<r){ int mid=(l+r)>>1; if(Stack[1][mid]<ind) l=mid+1; else r=mid; } return Stack[0][l]; } int main(int argc, char** argv) { int M;long long D; scanf("%d %lld",&M,&D); long long t=0; while(M--){ char c; long long d; cin>>c>>d; if(c=='A'){ add((t+d)%D); } else if(c=='Q'){ t=quiry(d); printf("%lld\n",t); } } return 0; }

 

線段樹解法,維護區間的最大值。

#include <iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=200010*4;
ll Max[maxn];

void add(int p,int l,int r,int x,ll c){
    if(l==r){
        Max[p]=c;return;
    }
    int mid=(l+r)>>1;
    if(x<=mid) add(p<<1,l,mid,x,c);
    else add(p<<1|1,mid+1,r,x,c);
    Max[p]=max(Max[p<<1],Max[p<<1|1]); 
}
int quiry(int p,int l,int r,int a,int b){
    if(a>r||b<l) return -1e8;
    if(a<=l&&r<=b){
        return Max[p];
    } 
    int mid=(l+r)>>1;
    return max(quiry(p<<1,l,mid,a,b),quiry(p<<1|1,mid+1,r,a,b));
}
int main(int argc, char** argv) {
    int M,D;
    scanf("%d %d",&M,&D);
    fill(Max,Max+maxn,-1e8);
    ll t=0;
    int x=0;
    while(M--){
        char c;int d;
        cin>>c>>d;
        if(c=='A'){
            x++;
            ll num=(t+d)%D;
            add(1,1,200010,x,num);
        }else if(c=='Q'){
            t=quiry(1,1,200010,x-d+1,x);
            printf("%lld\n",t);
        }
    }
    return 0;
}