放他一马
题目描述
小美会按照编号从小到大的顺序依次遇到 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]中最大值。
优化/注意点:考虑到上述转移方程只和前一轮相关,可以使用一维滚动数组进行压缩。
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]
#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; }