「洛谷P1198」 [JSOI2008]最大數 解題報告
阿新 • • 發佈:2018-12-19
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; }