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;
}
posted @ 2026-06-15 13:40  kzssCCC  阅读(12)  评论(0)    收藏  举报