2026牛客暑期多校10 C题思路分享(期望,组合数学,dp,拉格朗日插值)
题意
有 \(n\) 台机器,每台机器的奖金是 \([0,m]\) 内随机生成的整数,你可以在任意时刻知晓任意一台机器的奖金.
进行 \(k\) 轮游戏,第 \(j\) 轮你会拿走奖金第 \(a_j\) 大的机器的所有奖金,随后该台机器的奖金重置为 \([0,m]\) 内一个随机整数.
求拿到奖金总和的期望,模 \(10^9+7\).
\(1\le m,k \le 300\),\(1\le m \le 10^9\).
思路
记奖金总和为 \(S\),根据期望的线性性:
根据尾和公式:
交换求和:
令 \(F(i) = \sum_{j=1}^{k}{\Pr(x_j \ge i)}\),因此:
考虑求解 \(F(i)\).
固定 \(i\) 后,只需讨论奖金与 \(i\) 的相对关系即可,令 \(f_{i,j,c}\) 表示第 \(j\) 轮结束后恰好有 \(c\) 台机器奖金不小于 \(i\) 的概率.
每轮开始前,若 \(c\ge a_j\),则该轮获得的奖金不小于 \(i\),因此:
令机器重置后,新奖金不小于 \(i\) 的概率为 \(p_i\),小于 \(i\) 概率为 \(q_i\),则:
\(f_{i,j,c}\) 可以通过简单的 \(dp\) 得到.
观察 \(dp\) 的初始化:
以及每次转移都是乘上 \(p_i\) 或 \(q_i\),发现 \(f_{i,j,c}\) 是关于 \(i\) 最多 \(n+k-1\) 次的多项式,而 \(F(i)\) 是若干个 \(f_{i,j,c}\) 的和当然也是关于 \(i\) 最多 \(n+k-1\) 次的多项式.
令 \(G(t) = \sum_{i=1}^{t}{F(i)}\) 为 \(n+k-1\) 次多项式的前缀和,因此 \(G(t)\) 是 \(n+k\) 次多项式,通过 \(dp\) 求出 \(G(t)\) 的 \(n+k+1\) 项后拉格朗日插值求 \(G(m)\) 即可.
时间复杂度 \(\mathcal{O}(nk(n+k))\).
代码
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
template<int MOD>
struct modint {
int val;
modint() : val(0) {}
modint(long long v) {
val = v % MOD;
if (val < 0) val += MOD;
}
modint& operator++() { val = (val + 1 == MOD ? 0 : val + 1); return *this; }
modint& operator--() { val = (val == 0 ? MOD - 1 : val - 1); return *this; }
modint operator++(int) { modint res = *this; ++*this; return res; }
modint operator--(int) { modint res = *this; --*this; return res; }
modint& operator+=(const modint& o) { val += o.val; if (val >= MOD) val -= MOD; return *this; }
modint& operator-=(const modint& o) { val -= o.val; if (val < 0) val += MOD; return *this; }
modint& operator*=(const modint& o) { val = 1LL * val * o.val % MOD; return *this; }
modint& operator/=(const modint& o) { return *this *= o.inv(); }
friend modint operator+(modint a, const modint& b) { return a += b; }
friend modint operator-(modint a, const modint& b) { return a -= b; }
friend modint operator*(modint a, const modint& b) { return a *= b; }
friend modint operator/(modint a, const modint& b) { return a /= b; }
friend bool operator==(const modint& a, const modint& b) { return a.val == b.val; }
friend bool operator!=(const modint& a, const modint& b) { return a.val != b.val; }
modint operator-() const { modint res = *this; res.val = (res.val == 0 ? 0 : MOD - res.val); return res;};
modint operator+() const { return *this; };
modint qpow(long long p) const {
modint res = 1, a = *this;
while (p > 0) {
if (p & 1) res *= a;
a *= a;
p >>= 1;
}
return res;
}
modint inv() const {
return qpow(MOD - 2);
}
friend std::ostream& operator<<(std::ostream& os, const modint& m) { return os << m.val; }
friend std::istream& operator>>(std::istream& is, modint& m) { long long v; is >> v; m = modint(v); return is; }
};
using Z = modint<(int)1e9+7>;
struct binom{
int n;
vector<Z> fac,ifac;
binom(int _n){
n = _n;
fac.assign(n+1,0);
ifac.assign(n+1,0);
fac[0] = 1;
for (int i=1;i<=n;i++){
fac[i] = fac[i-1]*i;
}
ifac[n] = fac[n].inv();
for (int i=n-1;i>=0;i--){
ifac[i] = ifac[i+1]*(i+1);
}
}
Z C(int a,int b){
if (a<0 || a>n || b<0 || b>n || a<b) return 0;
return fac[a]*ifac[b]*ifac[a-b];
}
Z A(int a,int b){
if (a<0 || a>n || b<0 || b>n || a<b) return 0;
return fac[a]*ifac[a-b];
}
};
binom bn(300);
void solve(){
int n,m,k;
cin >> n >> m >> k;
vector<int> a(k+1);
for (int i=1;i<=k;i++){
cin >> a[i];
}
vector<Z> F(n+k+2);
for (int i=1;i<=n+k+1;i++){
vector<Z> dp(n+1);
Z p = Z(m-i+1)/(m+1);
Z q = Z(i)/(m+1);
vector<Z> facp(n+1,1),facq(n+1,1);
for (int i=1;i<=n;i++){
facp[i] = facp[i-1]*p;
facq[i] = facq[i-1]*q;
}
for (int c=0;c<=n;c++){
dp[c] = bn.C(n,c)*facp[c]*facq[n-c];
}
for (int j=1;j<=k;j++){
vector<Z> ndp(n+1);
for (int c=0;c<=n;c++){
if (c>=a[j]){
ndp[c] += dp[c]*p;
if (c-1>=0){
ndp[c-1] += dp[c]*q;
}
}
else{
if (c+1<=n){
ndp[c+1] += dp[c]*p;
}
ndp[c] += dp[c]*q;
}
}
for (int c=a[j];c<=n;c++){
F[i] += dp[c];
}
dp = ndp;
}
}
vector<Z> G(n+k+2);
for (int i=1;i<=n+k+1;i++){
G[i] = G[i-1]+F[i];
}
Z res = 0;
for (int i=1;i<=n+k+1;i++){
Z cur = 1;
for (int j=1;j<=n+k+1;j++){
if (i==j) continue;
cur *= Z(m-j)/(i-j);
}
cur *= G[i];
res += cur;
}
cout << res << '\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
// cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号