CF2200G 思路分享(期望,dp)
https://codeforces.com/problemset/problem/2200/G
题意概述
给定长度为 \(n\) 的操作序列和初始值 \(x\),操作为加减乘除.
随机打乱操作序列,按操作序列进行操作,求最终值的期望,模 \(10^9+7\).
\(1\le n \le 3000\).
思路
首先将减和除操作转换成加和乘,对于每个加操作,其贡献为自身的值乘后续所有乘操作值的乘积.
假设总共有 \(m\) 个乘操作,某个加操作后续有 \(k\) 个乘操作的概率为 \(\frac{1}{m+1}\),相当于在 \(m\) 个元素的间隔中插一个板.
现在想知道后续恰好有 \(k\) 个乘操作时,乘操作元素之积的期望,通过所有可能方案的乘积之和除以方案数量计算.
所有可能方案的乘积之和可以用背包 \(dp\) 计算,令 \(dp[k]\) 为所有子集大小为 \(k\) 的集合元素乘积的和.
可能的方案数量为 \(\binom{m}{k}\).
因此对每个加操作,记其值为 \(t\),贡献为
\[t\cdot \sum_{k=0}^{m}{\frac{dp[k]}{\binom{m}{k}}}
\]
右边的乘积部分可以提前计算,同时需要算上初始值的贡献.
时间复杂度 \(\mathcal{O}(n^2)\).
代码
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
class inverse{
public:
int n,MOD;
vector<ll> fac,inv;
ll qpow(ll a,ll b){
ll res = 1;
while (b){
if (b&1) res = res*a%MOD;
a = a*a%MOD;
b >>= 1;
}
return res;
}
inverse(int _n,int _MOD){
n = _n;
MOD = _MOD;
fac = inv = vector<ll>(n+1);
fac[0] = 1;
for (int i=1;i<=n;i++){
fac[i] = fac[i-1]*i%MOD;
}
inv[n] = qpow(fac[n],MOD-2);
for (int i=n-1;i>=0;i--){
inv[i] = inv[i+1]*(i+1)%MOD;
}
}
ll C(ll a,ll b){
if (a<b || a<0 || b<0) return 0;
return fac[a]*inv[b]%MOD*inv[a-b]%MOD;
}
ll A(ll a,ll b){
if (a<b || a<0 || b<0) return 0;
return fac[a]*inv[a-b]%MOD;
}
ll cal_inv(ll p){
return qpow(p,MOD-2);
}
};
const int MOD = 1e9+7;
const ll INF = 9e18;
inverse inv(3005,MOD);
void solve(){
int n;
ll x;
cin >> n >> x;
vector<pair<char,ll>> a(n+1);
vector<ll> b;
for (int i=1;i<=n;i++){
string s;
cin >> s;
a[i].first = s[0];
a[i].second = stoll(s.substr(1));
if (a[i].first=='-'){
a[i].first = '+';
a[i].second = -a[i].second;
}
else if (a[i].first=='/'){
a[i].first = 'x';
a[i].second = inv.qpow(a[i].second,MOD-2);
}
if (a[i].first=='x'){
b.push_back(a[i].second);
}
}
int m = b.size();
vector<ll> dp(m+1);
dp[0] = 1;
for (auto& v:b){
for (int j=m;j>=1;j--){
dp[j] = (dp[j]+dp[j-1]*v%MOD)%MOD;
}
}
ll res = 0;
ll mul = 1;
for (auto& v:b){
mul = mul*v%MOD;
}
res = (res+mul*x%MOD)%MOD;
ll temp = 0;
for (int k=0;k<=m;k++){
temp = (temp+dp[k]*inv.qpow(inv.C(m,k),MOD-2)%MOD)%MOD;
}
temp = temp*inv.qpow(m+1,MOD-2)%MOD;
ll add = 0;
for (int i=1;i<=n;i++){
if (a[i].first=='+'){
add = ((add+a[i].second)%MOD+MOD)%MOD;
}
}
res = (res+add*temp%MOD)%MOD;
cout << res << '\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号