题目地址:
https://www.luogu.com.cn/problem/P1198
题目描述:
现在请求你维护一个数列,要求提供以下两种操作:
1、查询操作。
语法:Q L
功能:查询当前数列中末尾
L
L
L个数中的最大的数,并输出这个数的值。
限制:
L
L
L不超过当前数列的长度。
(
L
>
0
)
(L > 0)
(L>0)
2、 插入操作。
语法:A n
功能:将
n
n
n加上
t
t
t,其中
t
t
t是最近一次查询操作的答案(如果还未执行过查询操作,则
t
=
0
t=0
t=0),并将所得结果对一个固定的常数
D
D
D取模,将所得答案插入到数列的末尾。
限制:
n
n
n是整数(可能为负数)并且在长整范围内。
注意:初始时数列是空的,没有一个数。
输入格式:
第一行两个整数,
M
M
M和
D
D
D,其中
M
M
M表示操作的个数,
D
D
D如上文中所述。接下来的
M
M
M行,每行一个字符串,描述一个具体的操作。语法如上文所述。
输出格式:
对于每一个查询操作,你应该按照顺序依次输出结果,每个结果占一行。
数据范围:
对于全部的测试点,保证
1
≤
M
≤
2
×
1
0
5
1 \leq M \leq 2 \times 10^5
1≤M≤2×105,
1
≤
D
≤
2
×
1
0
9
1 \leq D \leq 2 \times 10^9
1≤D≤2×109。
可以用ST表。设当前数列为 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an,设 f [ k ] [ c ] = max k − 2 c + 1 ≤ i ≤ k a i f[k][c]=\max_{k-2^c+1\le i\le k} a_i f[k][c]=maxk−2c+1≤i≤kai,那么查询最后 l l l个数的最大值可以通过 f f f以 O ( 1 ) O(1) O(1)时间得到,即为: max { f [ n ] [ c ] , f [ n − l + 2 c ] [ c ] } , c = ⌊ log 2 l ⌋ \max\{f[n][c],f[n-l+2^c][c]\},c=\lfloor\log_2l\rfloor max{f[n][c],f[n−l+2c][c]},c=⌊log2l⌋在末尾添加一个数 x x x的时候, f [ . ≤ n ] f[.\le n] f[.≤n]不需要变,只需要计算一下 f [ n + 1 ] f[n+1] f[n+1]就行了,这可以用查询操作来实现,因为 f [ n + 1 ] [ c ] f[n+1][c] f[n+1][c]就是 max { x , max n − 2 c + 1 < i ≤ n a i } \max\{x,\max_{n-2^c+1< i\le n} a_i\} max{x,maxn−2c+1<i≤nai},而括号中的后者可以用查询操作来实现。代码如下:
#include <iostream>
#include <cmath>
using namespace std;
const int N = 2e5 + 10;
int n, idx;
long f[N][20], D;
long query(int c) {
int k = log2(c);
return max(f[idx][k], f[idx - c + (1 << k)][k]);
}
void add(long x) {
f[idx + 1][0] = x;
for (int k = 1; 1 << k <= idx + 1; k++)
f[idx + 1][k] = max(x, query((1 << k) - 1));
idx++;
}
int main() {
scanf("%d%ld", &n, &D);
long t = 0, c;
char op;
while (n--) {
cin >> op >> c;
if (op == 'A') add((t + c) % D);
else printf("%ld\n", t = query(c));
}
}
添加操作时间复杂度 O ( log n ) O(\log n) O(logn), n n n为添加的时候数列长度,查询时间 O ( 1 ) O(1) O(1),空间 O ( n log n ) O(n\log n) O(nlogn)。