AtCoder 417-D 题解

题目传送门

\(\color{gold}{AtCoder 417-D}\)

题面:

高桥将会收到\(n\)个礼物

高桥有一个指数叫做 期望值\(x\)(不是数学的那个),“期望值”有一个初始值,每个礼物还有\(3\)个值,一个是高桥对这个礼物的满意度\(p_i\),还有就是两个值\(a_i,b_i\)
对于每一个礼物\(i\),高桥的“期望值”会发生如下的变化:

  • 如果这个高桥对这个礼物的满意度没有自己的“期望值"高(\(p_i < x\)),那么高桥的期望值将会减少\(b_i\)
  • 如果这个高桥对这个礼物的满意度大于等于自己的“期望值"(\(p_i \geq x\)),那么高桥的期望值将会增加\(a_i\)

现在提前告诉你每个礼物对应的三个值。

然后你需要解决 \(t\) 个问题,每个问题都会告诉你一个数字\(k\), 你要解决的问题是:当”期望值“的初始值是\(k\) ,那么最后高桥的期望值是多少。

\(p_i,a_i,b_i \leq 500 (1 \leq i \leq n)\)
\(n \leq 10000\)
\(k \leq 10^9\)

思路

这道题该怎么做呢?

可以提前预处理,定义\(f_{i,j}\)表示:直接从第\(i\)个礼物开始收取到\(n\),且开始前“期望值”为\(j\),那么收完之后“期望值”是多少。
初始化:首先,如果一个礼物都不收,那么最后的期望值就是最初的期望值,\(f_{n+1,j} = j (1 \leq j)\)
然后我们从后往前递推,\(i\)\(n\)枚举到\(1\)\(j\)\(0\)枚举到\(1000\)
如果\(p_i \geq j\) ,那么显然, \(f_{i,j} = f_{i+1,j+a_i}\)
如果\(p_i < j\),那么显然,\(f_{i,j} = f_{i+1,\max(0,j-b_i)}\)
这样就可以在\(10^7\)次计算量以内把\(f\)数组求出来了。

接下来的问题就是:

输入的初始值很大怎么办,我们只考虑到了\(1000\)啊。

由于\(p_i \leq 500(1 \leq i \leq n)\)

所以如果所有的\(b_i(1\leq i\leq n\))$ 的总和小于\(初始值-500\),那么答案一定是初始值-所有\(b_i\)的总和。

对于其他的情况:同样的,可以发现,如果初始值很大,那么一定会一直减减减,直到$ < 500\(,所以,我们可以对\)b\(做前缀和,然后用二分去求减到什么时候\)<500$,这个操作可以用upper_bound

减完之后,值就小了,就可以直接利用\(f\)数组。

#include <bits/stdc++.h>
using namespace std;
int p[10001];
int a[10001];
int b[10001];
int s[10001];
int f[10002][1001];
int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++)
    {
        cin >> p[i] >> a[i] >> b[i];
        s[i] = s[i - 1] + b[i]; // 前缀和
    }
    for (int i = 0; i <= 1000; i++)
    {
        f[n + 1][i] = i; // 初始化
    }
    for (int i = n; i >= 1; i--)
    {
        for (int j = 0; j <= 1000; j++)
        {
            // 推导
            if (p[i] >= j)
            {
                f[i][j] = f[i + 1][j + a[i]];
            }
            else
            {
                f[i][j] = f[i + 1][max(0, j - b[i])];
            }
        }
    }
    int q;
    cin >> q;
    while (q--)
    {
        int x;
        cin >> x;
        if (x - 500 > s[n]) // 减到最后还是大于500
        {
            cout << x - s[n] << '\n';
        }
        else
        {
            int t = upper_bound(s + 1, s + n + 1, x - 500) - s; // 找到减到哪里
            cout << f[t][x - s[t - 1]] << '\n'; 
        }
    }
    return 0;
}

完结撒花★,°:.☆(w)/$:.°★

posted @ 2025-08-03 17:28  MichaelZeng  阅读(18)  评论(0)    收藏  举报