【洛谷】P1198 最大数

题目地址:

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 1M2×105 1 ≤ D ≤ 2 × 1 0 9 1 \leq D \leq 2 \times 10^9 1D2×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]=maxk2c+1ikai,那么查询最后 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[nl+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,maxn2c+1<inai},而括号中的后者可以用查询操作来实现。代码如下:

#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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值