题解:AcWing 1275 最大数

【题目来源】

AcWing:1275. 最大数 - AcWing题库

【题目描述】

给定一个正整数数列\(a_1,a_2,...,a_n\), 每一个数都在\(0\sim p-1\)之间。可以对这列数进行两种操作:

1.添加操作:向序列后添加一个数,序列长度变成 \(n+1\)

2.询问操作:询问这个序列中最后 \(L\) 个数中最大的数是多少。

程序运行的最开始,整数序列为空。一共要对整数序列进行\(m\)次操作。

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

【输入】

第一行有两个正整数\(m,p\),意义如题目描述;接下来\(m\)行,每一行表示一个操作。

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

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

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

【输出】

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

【输入样例】

5 100
A 96
Q 1
A 97
Q 1
Q 2

【输出样例】

96
93
96

【算法标签】

《AcWing 1275 最大数》 #线段树#

【代码详解】

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N = 200010;  // 最大操作次数

int m, p;  // m: 操作次数, p: 模数

// 线段树节点结构体
struct Node
{
    int l, r;    // 节点管理的区间[l, r]
    int v;       // 区间[l, r]中的最大值
} tree[N * 4];   // 线段树数组(开4倍空间)

/**
 * 向上更新父节点信息
 * 用左右孩子的最大值更新当前节点的最大值
 * @param u 当前节点编号
 */
void pushup(int u)
{
    tree[u].v = max(tree[u << 1].v, tree[u << 1 | 1].v);
}

/**
 * 构建线段树
 * @param u 当前节点编号
 * @param l 当前节点管理的左边界
 * @param r 当前节点管理的右边界
 */
void build(int u, int l, int r)
{
    tree[u] = {l, r};  // 设置节点管理的区间
    
    // 如果是叶子节点,直接返回
    if (l == r)
        return;
    
    // 计算中点,递归构建左右子树
    int mid = l + r >> 1;
    build(u << 1, l, mid);        // 构建左子树
    build(u << 1 | 1, mid + 1, r); // 构建右子树
}

/**
 * 区间查询:查询区间[L, R]的最大值
 * @param u 当前节点编号
 * @param L 查询区间的左边界
 * @param R 查询区间的右边界
 * @return 区间[L, R]的最大值
 */
int query(int u, int L, int R)
{
    // 如果当前节点区间完全包含在查询区间内,直接返回节点值
    if (tree[u].l >= L && tree[u].r <= R)
        return tree[u].v;
    
    int mid = tree[u].l + tree[u].r >> 1;  // 计算中点
    int v = 0;  // 存储查询结果
    
    // 递归查询左右子树中与查询区间有交集的部分
    if (L <= mid)
        v = query(u << 1, L, R);
    if (R > mid)
        v = max(v, query(u << 1 | 1, L, R));
    
    return v;
}

/**
 * 单点修改:将位置x的值修改为v
 * @param u 当前节点编号
 * @param x 要修改的位置
 * @param v 新的值
 */
void modify(int u, int x, int v)
{
    // 找到叶子节点,直接修改
    if (tree[u].l == x && tree[u].r == x)
    {
        tree[u].v = v;
    }
    else
    {
        int mid = tree[u].l + tree[u].r >> 1;
        
        // 根据x的位置决定递归修改左子树还是右子树
        if (x <= mid)
            modify(u << 1, x, v);
        else
            modify(u << 1 | 1, x, v);
        
        // 修改后更新父节点的值
        pushup(u);
    }
}

int main()
{
    int n = 0;      // 当前已插入的数据个数
    int last = 0;   // 上一次查询的结果
    
    cin >> m >> p;  // 输入操作次数和模数
    
    // 构建线段树,初始管理区间为[1, m]
    build(1, 1, m);
    
    char op[2];     // 操作类型
    int x;          // 操作参数
    
    // 处理每个操作
    while (m--)
    {
        cin >> op >> x;
        
        if (op[0] == 'Q')  // 查询操作
        {
            // 查询最后x个数的最大值
            last = query(1, n - x + 1, n);
            cout << last << endl;
        }
        else  // 插入操作('A')
        {
            // 在n+1位置插入新值:(last + x) % p
            LL new_val = ((LL)last + x) % p;
            modify(1, n + 1, new_val);
            n++;  // 数据个数加1
        }
    }
    
    return 0;
}

【运行结果】

5 100
A 96
Q 1
96
A 97
Q 1
93
Q 2
96
posted @ 2026-03-10 14:56  团爸讲算法  阅读(4)  评论(0)    收藏  举报