放他一马
题目描述
小美会按照编号从小到大的顺序依次遇到 n 只怪物(编号为 1 ~ n),怪物 i(1 ≤ i ≤ n) 的生命为 ai。对于每只怪物,小美都可以选择放走 Ta 或者击败 Ta。如果放走怪物,小美将获得 i 点经验值。如果击败怪物,小美将获得 ai 点经验值,同时将额外获得 (x mod 10) × ai 点经验值,x 为击败怪物数量(包括这一个怪物)。求小美最多可以从这 n 个怪物中获得的经验值。
输入
第一行输入一个整数 n(1 ≤ n ≤ 2 × 105) 表示怪物数。第二行输入 n 个整数 ai(1 ≤ ai ≤ 109) 表示怪物的生命。
输出
输出一个整数表示小美可以获得最高的经验值。
示例1
输入
3
5 3 2
输出
27
思路和代码
实现:动态规划
-
设定
f[i][j]表示为前i只怪物,击败j只怪兽(j mod 10)能够获得的最大经验。 -
状态转移方程:
f[i][j] = max(f[i-1][j] + i, f[i-1][(j-1 + 10) %10] + i *(num+1)),分别表示放走和击败,取其中较大值。 -
最终答案就是
f[n][j], j取值范围[1,9]中最大值。
优化/注意点:考虑到上述转移方程只和前一轮相关,可以使用一维滚动数组进行压缩。
CPP
import sys
def main():
n = int(sys.stdin.readline().strip())
a = list(map(int, sys.stdin.readline().split()))
dp = [0] * 10
dp_last = [0] * 10
for i in range(1, n + 1):
num = a[i - 1]
for j in range(10):
if j <= i:
dp[j] = max(dp_last[j] + i,
dp_last[(j - 1 + 10) % 10] + j * num + num)
dp_last = dp[:]
res = max(dp_last)
print(res)
if __name__ == "__main__":
main()
信号模拟
题目描述
如下所示,有 (2 × n) 个仪器,中间的方块是仪器的主体,每个仪器可以充当接收器或者信号源;主体的左右两侧是两个接线点。现在,我们将左端 (2 × n) 个接线点随机分成 (n) 组,每组各含两个点,并将右端 (2 × n) 个接线点同样随机分成 (n) 组。然后将每组的两个接线点用导线连接。这样一来,我们就得到了一组封闭的信号线路。具体而言:
- 信号从任一信号源 (i) 出发,通过右侧接线点;
- 随后,信号通过与右侧接线点连接的导线到达另外一个仪器的左侧接线点,再经过仪器主体到达右侧接线点;
- 此时,如果这个仪器是接收器,那么就视为接收到了信号(注意,接收到信号不会影响信号继续往后传递)。
- 这个过程持续进行,最终会形成若干个独立的循环。
现在,记 (x) 表示在所有接收器均能接收到信号的前提下,(2 × n) 个仪器中作为信号源的最少数数量。求解 (x) 的方差。可以证明答案可以表示为一个不可约分数 (p/q),为了避免精度问题,请直接输出整数 (p × q-1 mod M) 作为答案,其中 (M = 998244353),q-1 是满足 q × q-1 ≡ 1 (mod M) 的整数。
输入
每个测试文件均包含多组测试数据。第一行输入一个整数 (T (1 <= T <= 10^4)) 代表数据组数,每组测试数据描述如下:在一行上输入一个整数 (n (1 <= n <= 10^6)) 代表仪器的数量。
输出
对于每组测试数据,新起一行输出一个整数,表示 (x) 的方差对 (M = 998244353) 取模后的结果。
提示
本题中如果需要使用除法的取模,即计算(p * q ^-1 mod M)时,(q^-1)需要使用公式((q^ M-2 MOD M))得到,
示例1
输入
3
1
2
3
输出
0
887328314
168592380
思路和代码
实现:乘法逆元 + 前缀和
- 设X为最少信号源数,E(x)为期望,E(x ^2)为二次期望,D(X) = E(x ^2) - E(x) ^2
- 设
inv[k]为k的逆元,设置total1,total1[n]为1 - n的1/2(j -1)的前缀和,设置total2,total2[n]为1 - n的(1/2(j -1))^2的前缀和, - 初始化预计算逆元和前缀和
- 对于输入为n的情况下结果即为
total[n] - total2[n]
CPP
#include<iostream>
#include<vector>
#include<string>
#include <utility>
#include<algorithm>
#include<cmath>
#include<map>
#include<stack>
#include<unordered_map>
using namespace std;
typedef long long LL ;
const int MOD = 998244353, N = 1e6 + 10;
// inv[i] 等于 i在MOD下的逆元
LL inv[2 * N];
// total1[i] 表示 1/(2i-1)的前缀和
LL total1[N];
// total2[i]表示(1/(2i-1))^2的前缀和
LL total2[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int T;
cin >> T;
int input[T],maxv = 0;
for (int i = 0; i < T;i++) {
cin >> input[i];
maxv = max(maxv, input[i]);
}
// 最大逆元下标
int maxv2 = 2 * maxv;
inv[1] = 1;
// 递推
for (int i = 2; i <= maxv2; i++) {
inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD;
}
// 预处理前缀和
total1[0] = 0;
total2[0] = 0;
// 预计算前缀和
for (int j = 1; j <= maxv; j++) {
LL x = inv[2 * j - 1];
total1[j] = (total1[j - 1] + x) % MOD;
total2[j] = (total2[j-1] + x * x % MOD) %MOD;
}
for (int i = 0; i < T; i++) {
int n = input[i];
// 方差公式 D(X) = sum1[n] = sum2[2]
long long res = (total1[n] - total2[n] + MOD) % MOD;
cout << res << endl;
}
return 0;
}