CF1842G Tenzing and Random Operations 题解
思路
牛逼。
好吧还是推式子。
首先会发现这些操作后,\(a_i\) 会变成 \((a_i + v + v + \cdots)\),即原数加上一堆 \(v\)。不好直接维护,因为 \(m \le 10^9\),所以考虑拆贡献。用乘法分配律将这个式子展开,即将 \(\prod \limits_{i = 1}^n (a_i + v + v + \cdots)\) 展开,对于这 \(n\) 个数中每一个数都选一项。最后会得到形如一堆原数乘上一堆 \(v\),最后再相加。
钦定的时候有三种情况:
- 选择原数 \(a_i\)。贡献为 \(a_i\);
- 选择之前已经被钦定的 \(v\),也就是在前 \(i - 1\) 个数的选择中,有一些(个)数也选了这个同样的 \(v\),贡献为 \(j \times v\),其中 \(j\) 为之前用过的 \(v\);
- 选择之前未被钦定的 \(v\),此时有 \(m - j\) 个 \(v\) 没有被钦定,同时还需要钦定这个 \(v\) 的位置。因为每一个 \(v\) 会影响其后缀,所以有 \(i\) 个位置可以被钦定(即 \(1 \sim i\))。综上,贡献为 \((m - j) \times i \times v\),其中 \(j\) 为之前用过的 \(v\);
这样就可以 DP 了。设 \(dp_{i, j}\) 代表 \(1 \sim i\) 这个前缀中,钦定的数中有 \(j\) 个 \(v\) 时这部分的贡献。那直接 DP:
- 选择原数:\(dp_{i + 1, j} \leftarrow dp_{i, j} \times a_i\);
- 选择已被钦定的 \(v\):\(dp_{i + 1, j} \leftarrow dp_{i, j} \times j \times v\);
- 选择未被钦定的 \(v\):\(dp_{i + 1, j + 1} \leftarrow dp_{i, j} \times (m - j) \times i \times v\)。
最后枚举被钦定的 \(v\) 的个数 \(0 \le j \le \min(n, m)\),答案即为:
\[\sum_{j = 0}^{\min(n, m)}\frac{dp_{n, j} \times n ^ {m - j}}{n ^ m}
\]
其中乘上 \(n ^ {m - j}\) 的意思是对于剩下未被钦定的 \(v\),可以随便放,对答案没有贡献。除以 \(n ^ m\) 是为了算期望。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 5005, mod = 1e9 + 7;
int n, m, v;
int a[N], dp[N][N];
int fastPow(int a, int b)
{
int res = 1;
while (b)
{
if (b & 1) res = res * a % mod;
a = a * a % mod;
b >>= 1;
}
return res;
}
signed main()
{
scanf("%lld%lld%lld", &n, &m, &v);
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
dp[0][0] = 1;
for (int i = 1; i <= n; i++)
{
for (int j = 0; j <= min(m, i); j++)
{
dp[i][j] = (dp[i][j] + dp[i - 1][j] * a[i]) % mod;
dp[i][j] = (dp[i][j] + (dp[i - 1][j] * j % mod) * v) % mod;
dp[i][j + 1] = (dp[i][j + 1] + (((i * (m - j) % mod) * v % mod) * dp[i - 1][j])) % mod;
}
}
int ans = 0;
for (int i = 0; i <= min(n, m); i++)
ans = (ans + dp[n][i] * fastPow(n, m - i)) % mod;
ans = ans * fastPow(fastPow(n, m), mod - 2) % mod;
printf("%lld\n", ans);
return 0;
}

浙公网安备 33010602011771号