CF1556F-Sports Betting【状压dp,数学期望】

正题

题目链接:https://www.luogu.com.cn/problem/CF1556F


题目大意

n n n个点的一张竞赛图,每个点有一个权值 a i a_i ai ( i , j ) (i,j) (i,j)之间的边 i i i j j j的概率是 a i a i + a j \frac{a_i}{a_i+a_j} ai+ajai,否则 j j j i i i

现在期望有多少个点能走到全图的任意一个点。

1 ≤ n ≤ 14 , 1 ≤ a i ≤ 1 0 6 1\leq n\leq 14,1\leq a_i\leq 10^6 1n14,1ai106


解题思路

考虑状压 d p dp dp,首先枚举起点 p p p,设 f S f_{S} fS表示目前只考虑了点集 S S S p p p都能到达。

那么对于点集 S S S是任意一张图的概率是 1 1 1,然后考虑枚举一个 p p p能到达的集合 T T T之后其他点 p p p都不能到达,为了方便表示下面记 g S , T g_{S,T} gS,T表示点集 S S S T T T之间的边都是 S S S指向 T T T的概率那么有
1 = ∑ T ⊆ S f T × g S − T , T 1=\sum_{T\subseteq S}f_T\times g_{S-T,T} 1=TSfT×gST,T
⇒ f S = 1 − ∑ T ⊂ S f T × g S − T , T \Rightarrow f_S=1-\sum_{T\subset S}f_T\times g_{S-T,T} fS=1TSfT×gST,T

考虑如何预处理 g S , T g_{S,T} gS,T,不难发现因为 S ∩ T = ∅ S\cap T=\varnothing ST=所以这个状态数是 3 n 3^n 3n的我们可以用三进制状压,不过得先预处理 r p , S r_{p,S} rp,S表示 p p p与集合 S S S之间的边都是 p p p连向 S S S的概率。

时间复杂度: O ( 3 n n + 2 n n 2 ) O(3^nn+2^nn^2) O(3nn+2nn2)


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=14,M=2e6+10,P=1e9+7;
ll n,ans,inv[M],pw[N+1],a[N],r[N][1<<N],tr[1<<N],f[1<<N],g[4782969];
signed main()
{
	inv[1]=1;
	for(ll i=2;i<M;i++)inv[i]=P-(P/i)*inv[P%i]%P;
	scanf("%lld",&n);
	for(ll i=0;i<n;i++)
		scanf("%lld",&a[i]);
	ll MS=(1<<n);
	for(ll p=0;p<n;p++){
		r[p][0]=1;
		for(ll s=0;s<MS;s++){
			if((s>>p)&1)continue;
			for(ll i=0;i<n;i++)
				if((s>>i)&1){r[p][s]=r[p][s^(1<<i)]*a[p]%P*inv[a[p]+a[i]]%P;break;}
		}
	}
	pw[0]=1;for(ll i=1;i<=n;i++)pw[i]=pw[i-1]*3;
	for(ll s=1;s<MS;s++)
		for(ll i=0;i<n;i++)
			if((s>>i)&1)tr[s]=tr[s^(1<<i)]+pw[i];
	for(ll s=0;s<pw[n];s++)g[s]=1;
	for(ll s=0;s<MS;s++)
		for(ll i=0;i<n;i++){
			if(!((s>>i)&1))continue;
			for(ll t=s;t;t=(t-1)&s){
				if((t>>i)&1)continue;
				(g[tr[s]+tr[t]]*=r[i][t])%=P;
			}
		}
	for(ll p=0;p<n;p++){
		memset(f,0,sizeof(f));
		for(ll s=0;s<MS;s++){
			if(!((s>>p)&1))continue;f[s]=1;
			for(ll t=(s-1)&s;t;t=(t-1)&s){
				if(!((t>>p)&1))continue;
				(f[s]+=P-f[t]*g[tr[s]+tr[t]]%P)%=P;
			}
		}
		(ans+=f[MS-1])%=P;
	}
	printf("%lld\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值