[JSOI2008] 最大数题解

题面

题目描述

现在请求你维护一个数列,要求提供以下两种操作:

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

样例输入 #1

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

样例输出 #1

96
93
96

提示

数据规模与约定

对于全部的测试点,保证 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

思路

不难看出,这题可以使用线段树来做(用于维护区间最大值),我们先建一棵树,让它的所有叶子节点全部都为 0 0 0。当我们每次输入为 A 时,就更改下一个叶子节点,这就变成了单点修改。如果输入为 Q 时,就用区间查找的方法寻找末尾 L L L 个数中的最大的数。

建树

inline void make_tree(int q, int l, int r) {//建树
    if (l == r) {//如果是叶子节点
        tree[q].left = l;//左端为自己的位置
        tree[q].right = r;//右端为自己的位置
        tree[q].maxnum = 0;//因为还不知到值是多少,先为0,后面再修改
        return;
    }
    int mid = (l + r) >> 1;
    //将l~r分成l~mid和(mid+1)~r,类似于分治,利用小问题的解求出大问题
    make_tree(q * 2, l, mid);
    make_tree(q * 2 + 1, mid + 1, r);
    tree[q].left = l;
    tree[q].right = r;
    tree[q].maxnum = max(tree[q * 2].maxnum, tree[q * 2 + 1].maxnum);
}

单点修改

inline void change(int q, int x, int sum) {//单点修改
    if (tree[q].left == x && x == tree[q].right) {//寻找到该点了
        tree[q].maxnum += sum;//将值加上,因为初始话为 0,也可以改成tree[q].maxnum = sum
        return;
    }
    if (x >= tree[q * 2 + 1].left)//在右半边
        change(q * 2 + 1, x, sum);
    else if (x <= tree[q * 2].right)//在左半边
        change(q * 2, x, sum);
    tree[q].maxnum = max(tree[q * 2].maxnum, tree[q * 2 + 1].maxnum);//更新
}

区间查找

inline int getmax(int q, int l, int r) {//区间查找
    if (l == tree[q].left && r == tree[q].right)//找到一块符合的
        return tree[q].maxnum;
    if (l >= tree[q * 2 + 1].left)//全部都在右半边
        return getmax(q * 2 + 1, l, r);
    else if (r <= tree[q * 2].right)//全部都在左半边
        return getmax(q * 2, l, r);
    else//都有
        return max(getmax(q * 2, l, tree[q * 2].right), getmax(q * 2 + 1, tree[q * 2 + 1].left, r));
}

Code

#include <bits/stdc++.h>
using namespace std;
#define int long long
int n, m;
int last;
struct op {
    int left, right, maxnum;
} tree[5000005];
inline int read() {
    int x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
inline void make_tree(int q, int l, int r) {//建树
    if (l == r) {
        tree[q].left = l;
        tree[q].right = r;
        tree[q].maxnum = 0;
        return;
    }
    int mid = (l + r) >> 1;
    make_tree(q * 2, l, mid);
    make_tree(q * 2 + 1, mid + 1, r);
    tree[q].left = l;
    tree[q].right = r;
    tree[q].maxnum = max(tree[q * 2].maxnum, tree[q * 2 + 1].maxnum);
}
inline int getmax(int q, int l, int r) {//区间查找
    if (l == tree[q].left && r == tree[q].right)
        return tree[q].maxnum;
    if (l >= tree[q * 2 + 1].left)
        return getmax(q * 2 + 1, l, r);
    else if (r <= tree[q * 2].right)
        return getmax(q * 2, l, r);
    else
        return max(getmax(q * 2, l, tree[q * 2].right), getmax(q * 2 + 1, tree[q * 2 + 1].left, r));
}
inline void change(int q, int x, int sum) {//单点修改
    if (tree[q].left == x && x == tree[q].right) {
        tree[q].maxnum += sum;
        return;
    }
    if (x >= tree[q * 2 + 1].left)
        change(q * 2 + 1, x, sum);
    else if (x <= tree[q * 2].right)
        change(q * 2, x, sum);
    tree[q].maxnum = max(tree[q * 2].maxnum, tree[q * 2 + 1].maxnum);
}
int www;
signed main() {
    n = read(), m = read();
    make_tree(1, 1, n);
    while (n--) {
        char aa = getchar();
        if (aa == 'A') {
            int xx = read();
            xx = (xx + last) % m;
            www++;
            change(1, www, xx);
        } else {
            int xx = read();
            last = getmax(1, www - xx + 1, www);
            printf("%lld\n", last);
        }
    }
    return 0;
}
根据引用[1],dp[u][j]表示在u子树中选取恰好j个人时能获得的最大价值。而根据引用,该问题的时间复杂度为O(log2​104×nm)。 对于洛谷P2143 [JSOI2010] 巨额奖金问题,我们可以使用动态规划来解决。具体步骤如下: 1. 首先,我们需要构建一棵树来表示员工之间的关系。树的根节点表示公司的总经理,其他节点表示员工。每个节点都有一个权值,表示该员工的奖金金额。 2. 接下来,我们可以使用动态规划来计算每个节点的dp值。对于每个节点u,我们可以考虑两种情况: - 如果选择节点u,则dp[u][j] = dp[v][j-1] + value[u],其中v是u的子节点,value[u]表示节点u的奖金金额。 - 如果不选择节点u,则dp[u][j] = max(dp[v][j]),其中v是u的子节点。 3. 最后,我们可以通过遍历树的所有节点,计算出dp[u][j]的最大值,即为所求的巨额奖金。 下面是一个示例代码,演示了如何使用动态规划来解决洛谷P2143 [JSOI2010] 巨额奖金问题: ```python # 构建树的数据结构 class Node: def __init__(self, value): self.value = value self.children = [] # 动态规划求解最大奖金 def max_bonus(root, j): dp = [[0] * (j+1) for _ in range(len(root)+1)] def dfs(node): if not node: return for child in node.children: dfs(child) for k in range(j, 0, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-1] + node.value) for child in node.children: for k in range(j, 0, -1): for l in range(k-1, -1, -1): dp[node.value][k] = max(dp[node.value][k], dp[node.value][k-l-1] + dp[child.value][l]) dfs(root) return dp[root.value][j] # 构建树 root = Node(1) root.children.append(Node(2)) root.children.append(Node(3)) root.children[0].children.append(Node(4)) root.children[0].children.append(Node(5)) root.children[1].children.append(Node(6)) # 求解最大奖金 j = 3 max_bonus_value = max_bonus(root, j) print("最大奖金为:", max_bonus_value) ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值