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;
}