1. 程式人生 > >「洛谷P1198」 [JSOI2008]最大數 解題報告

「洛谷P1198」 [JSOI2008]最大數 解題報告

P1198 [JSOI2008]最大數

題目描述

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

1、 查詢操作。

語法:Q L

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

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

2、 插入操作。

語法:A n

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

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

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

輸入輸出格式

輸入格式:

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

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

輸出格式:

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

輸入輸出樣例

輸入樣例#1:

5 100
A 96
Q 1
A 97
Q 1
Q 2

輸出樣例#1:

96
93
96

說明

[JSOI2008]
本題資料已加強


思路

很明顯,這道題的一般思路是線段樹。但是,這道題用ST表也可以做!!!

一般來說,我們寫ST表都是\(f[i][j] =\max\text{{ a[i] , a[i+1],······,a[i+(1<<j)-1]}}\)

但是,它是從後插入元素的,如果按照一般思路,正著做的話,最壞情況下就要改\(nlgn\) 次,還不如暴力呢!但是,我們可以倒過來\(f[i][j] = \max\text{{a[i] , a[i-1] ,······, a[i - (i<<j)+1]}}\),這樣,每次插入僅需修改\(lgn\)個元素了。

這種解法十分神奇,結束了“ST表不能修改”的歷史。在這道題中,合理地使用ST表還是能得出很簡潔的解法的。

程式碼

#include<bits/stdc++.h>
using namespace std;
#define MAXN 200005
#define LL long long

int M;
LL D;
char opt;
LL t, x;
LL a[MAXN], f[MAXN][20], N;

int main(){
    scanf( "%d%lld", &M, &D );
    while ( M-- ){
        while( ( opt = getchar() ) != 'A' && opt != 'Q' );
        scanf( "%lld", &x );
        if ( opt == 'A' ){
            a[++N] = ( x + t ) % D;
            f[N][0] = a[N];
            for ( int i = 1; ( 1 << i ) <= N; ++i ) f[N][i] = max( f[N][i - 1], f[N - (1 << ( i - 1 ))][i - 1] );
        }else{
            int len = min( x, N ); x = N - len + 1;
            int tt((LL)floor(log(len) / log(2)));
            printf( "%lld\n", ( t = max( f[N][tt], f[x + ( 1 << tt ) - 1][tt] ) ) );
        }

    }
    return 0;
}