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)/$:.°★ 。

浙公网安备 33010602011771号