CF1842G Tenzing and Random Operations 题解
Description
有一长度为 \(n\) 的序列 \(a\) 和一整数 \(v\),对该序列进行 \(m\) 次操作。
每次操作中,等概率地随机选择一整数 \(i \in [1, n]\),对所有的 \(j \in [i, n]\),赋值 \(a_j \gets a_j+v\),求出 \(m\) 次操作后 \(\Pi_{i=1}^n a_i\) 的期望值,对 \(10^9+7\) 取模。
数据范围:\(0\leq n \leq 5000\),\(1 \leq m, v \leq10^9\),\(a_i \in [1, 10^9] \cap \mathbb{Z}\)。
Solution
设 \(b_i\) 表示第 \(i\) 次操作随机选择的数,那么答案形如:
这个东西显然是可以拆开的,最终答案一定形如一些 \(a_i\) 的乘积和某些 \([b_j\leq i]v\) 的乘积。
假设 \([b_j\leq i]v\) 选的下标是 \(i_1<i_2<\ldots<i_k\),如果 \(b_j>i_1\),则乘积为 \(0\),没有贡献。否则一定有贡献,所以选的 \(b_j\) 只需要满足 \(b_j\) 不超过第一个选 \(j\) 的下标即可。
这样就可以 dp 了。
设 \(f_{i,j}\) 表示 \([1,i]\) 的前缀,目前已经选好了 \(j\) 的 \(b_k\) 的位置。如果这一位选了 \(a_i\),则 \(f_{i,j}\leftarrow a_if_{i-1,j}\)。如果这一位选了之前选过的 \(b_k\),则 \(f_{i,j}\leftarrow jvf_{i-1,j}\)。如果这一位选了一个新的 \(b_k\),则 \(f_{i,j+1}\leftarrow (m-j)iv\cdot f_{i-1,j}\),这里的 \(i\) 是从 \([1,i]\) 中选一个作为 \(b_k\) 的方案数。
最终答案即为 \(\sum \frac{f_{n,i}n^{m-i}}{n^m}\)。
时间复杂度:\(O(n^2)\)。
Code
#include <bits/stdc++.h> // #define int int64_t const int kMaxN = 5e3 + 5, kMod = 1e9 + 7; int n, m, v; int a[kMaxN], f[kMaxN][kMaxN]; constexpr int qpow(int bs, int64_t idx = kMod - 2) { int ret = 1; for (; idx; idx >>= 1, bs = (int64_t)bs * bs % kMod) if (idx & 1) ret = (int64_t)ret * bs % kMod; return ret; } inline int add(int x, int y) { return (x + y >= kMod ? x + y - kMod : x + y); } inline int sub(int x, int y) { return (x >= y ? x - y : x - y + kMod); } inline void inc(int &x, int y) { (x += y) >= kMod ? x -= kMod : x; } inline void dec(int &x, int y) { (x -= y) < 0 ? x += kMod : x; } void dickdreamer() { std::cin >> n >> m >> v; for (int i = 1; i <= n; ++i) std::cin >> a[i]; f[0][0] = 1; for (int i = 1; i <= n; ++i) { for (int j = 0; j <= std::min(n, m); ++j) { inc(f[i][j], 1ll * a[i] * f[i - 1][j] % kMod); inc(f[i][j], 1ll * j * v % kMod * f[i - 1][j] % kMod); inc(f[i][j + 1], 1ll * (m - j) * i % kMod * v % kMod * f[i - 1][j] % kMod); } } int ans = 0; for (int i = 0; i <= std::min(n, m); ++i) inc(ans, 1ll * f[n][i] * qpow(n, m - i) % kMod); std::cout << 1ll * ans * qpow(qpow(n), m) % kMod << '\n'; } int32_t main() { #ifdef ORZXKR freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); #endif std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0); int T = 1; // std::cin >> T; while (T--) dickdreamer(); // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n"; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2023-10-02 后缀数组学习笔记
2022-10-02 [Violet 5]列队春游 题解