You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5.9 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

##AcWing 1275. 最大数

一、题目描述

给定一个正整数数列 a_1,a_2,…,a_n,每一个数都在 0p1 之间。

可以对这列数进行两种操作:

添加操作:向序列后添加一个数,序列长度变成 n+1 询问操作:询问这个序列中最后 L 个数中最大的数是多少。

程序运行的最开始,整数序列为空。

一共要对整数序列进行 m 次操作。

写一个程序,读入操作的序列,并输出询问操作的答案。

输入格式 第一行有两个正整数 m,p,意义如题目描述;

接下来 m 行,每一行表示一个操作。

如果该行的内容是 Q L,则表示这个操作是询问序列中最后 L 个数的最大数是多少;

如果是 A t,则表示向序列后面加一个数,加入的数是 (t+a) mod p。其中,t 是输入的参数,a 是在这个添加操作之前最后一个询问操作的答案(如果之前没有询问操作,则 a=0)。

第一个操作一定是添加操作。对于询问操作,L>0 且不超过当前序列的长度。

输出格式 对于每一个询问操作,输出一行。该行只有一个数,即序列中最后 L 个数的最大数。

数据范围 1≤m≤2×10^5,1≤p≤2×10^9,0≤t<p

输入样例

10 100
A 97
Q 1
Q 1
A 17
Q 2
A 63
Q 1
Q 1
Q 3
A 99

输出样例

97
97
97
60
60
97

样例解释 最后的序列是 97,14,60,96

二、树状数组解法

运行时间: 246 ms 重点: 因为每次询问的区间是最后几个数字,而树状数组可以快速求前缀,所以不妨倒着存储数据

为了减少时间复杂度,要提前预留m个坑位,第一个数存放到m,第二个数存放到m-1,以此类推

#include <iostream>
#include <cstdio>
typedef long long LL;
using namespace std;
const int N = 2e5 + 10;
int tr[N];

int m, p;

#define lowbit(x) (x & -x)
void add(int n, int x) {
    for (int i = n; i <= m; i += lowbit(i)) tr[i] = max(tr[i], x);
}
int query(int n) {
    int ret = 0;
    for (int i = n; i; i -= lowbit(i)) ret = max(ret, tr[i]);
    return ret;
}

int main() {
    //加快读入
    ios::sync_with_stdio(false), cin.tie(0);
    cin >> m >> p;
    int last = 0;
    int j = m;
    for (int i = 0; i < m; i++) {
        LL x;
        char c;
        cin >> c >> x;
        if (c == 'A')
            add(j--, (x + last) % p);
        else {
            last = query(j + x);
            printf("%d\n", last);
        }
    }
    return 0;
}

三、线段树求最大值模板代码

运行时间:646 ms

#include <bits/stdc++.h>

using namespace std;
typedef long long LL;
const int N = 200010;
int m; // m个操作
int p; // mod p

// 线段树求最大值模板
#define ls (u << 1)
#define rs (u << 1 | 1)
#define mid ((l + r) >> 1)
struct Node {
    int l, r, len;
    int v;
} tr[N << 2];

void pushup(int u) {
    tr[u].v = max(tr[ls].v, tr[rs].v);
}

void build(int u, int l, int r) {
    tr[u].l = l, tr[u].r = r;
    if (l == r) return;
    build(ls, l, mid), build(rs, mid + 1, r);
}

int query(int u, int L, int R) {
    int l = tr[u].l, r = tr[u].r;
    if (l >= L && r <= R) return tr[u].v;
    if (l > R || r < L) return 0;
    return max(query(ls, L, R), query(rs, L, R));
}

void modify(int u, int x, int v) {
    int l = tr[u].l, r = tr[u].r;
    if (l == r) {
        tr[u].v = v;
        return;
    }
    if (x <= mid)
        modify(u << 1, x, v);
    else
        modify(u << 1 | 1, x, v);
    pushup(u);
}

int n, last;

int main() {
    // 加快读入
    ios::sync_with_stdio(false), cin.tie(0);
    cin >> m >> p;

    // 单点修改,求区间最大值

    // ① 初始化线段树,最多m次操作最多m个数区间范围[1, m]
    build(1, 1, m);

    int x;
    char op;
    while (m--) {
        cin >> op >> x;
        if (op == 'A') {
            // 对于节点++n进行修改,值=(最后一次的last查询值 + x )%p
            modify(1, ++n, ((LL)last + x) % p);
        } else {
            // u = 1:从根节点开始查询
            // 查询序列中最后x个数的最大数: 查询[n - x + 1, n]内的最大值
            last = query(1, n - x + 1, n);
            printf("%d\n", last);
        }
    }
    return 0;
}

四、ST表解法

大部分大佬用的是线段树(我第一次也用的线段树) 现在发现用st超级容易 每次插入只需要修改与最后一个点有关的值即可 典型的空间换时间; 线段树空间复杂度O(4N),查询插入都是O(logN),不过时间里面还有许多浪费的成分 st空间复杂度O(NlogN),查询O(1),插入O(logN),比线段树快至少一半

由于st表还没有复习到,这里先不深入研究st表的解法,后面第三刷时再来仔细研究:

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
const int N=2e5+10,M=log2(N)+10;
int f[N][M];
int query(int l,int r){
    int k=log2(r-l+1);
    return max(f[l][k],f[r-(1<<k)+1][k]);
}
int n;
int insert(int a){
    f[++n][0]=a;
    for(int i=1;1<<i<=n;i++)
        f[n-(1<<i)+1][i]=max(f[n-(1<<i)+1][i-1],f[n-(1<<i-1)+1][i-1]);
}
int q,p,a;
char op[2];
int x;
int main(){
    scanf("%d%d",&q,&p);
    for(;q--;){
        scanf("%s%d",op,&x);
        if(op[0]=='A')
            insert((ll(x)+a)%p);
        else{
            printf("%d\n",a=query(n-x+1,n));
        }
    }
    return 0;
}