[JZOJ 5957] scarborough fair【状压DP】【图论】

29 篇文章 0 订阅
25 篇文章 0 订阅

Description

给定一个n个点m条边的无向图
每条边有一个不出现的概率(不为0),求连通块数的期望
无重边自环
答案对998244353取模
n ≤ 17 , m ≤ n ( n − 1 ) 2 n\leq 17,m\leq {n(n-1)\over 2} n17,m2n(n1)

Solution

考虑单独计算每个连通块的贡献,我们可以枚举一个点集,然后令它自成一个连通块,算出概率,乘上它的所有出边不出现的概率,这就是这个连通块对总期望的贡献,直接累加。

那么现在的问题就是,给定一个点集S,我们需要求出点集S的导出子图(即内部的边+点)连通的概率。

这就是套路了,我们考虑容斥,用1-不连通的概率和。
F [ S ] F[S] F[S]为点集S内部连通的概率。
枚举其中标号最小的点i所在的连通块点集T,要求 T ⫋ S , i ∈ T T\subsetneqq S,i\in T TS,iT,这可以采用子集枚举,时间复杂度是 O ( 3 n ) O(3^n) O(3n)
那么现在还需要计算的是 T T T S − T S-T ST之间的边不出现概率之积
如果暴力统计,那么复杂度要多乘个n

然而有个非常妙的方法。
G [ S ] G[S] G[S]为点集 S S S的导出子图中的边的不出现概率之积
那么 T T T S − T S-T ST之间的边不出现概率之积就是 G [ S ] G [ T ] × G [ S − T ] {G[S]\over G[T]\times G[S-T]} G[T]×G[ST]G[S]

因此枚举T,就有 F [ S ] = 1 − ∑ T ⫋ S , l o w b i t ( S ) ∈ T F [ T ] × G [ S ] G [ T ] × G [ S − T ] F[S]=1-\sum\limits_{T\subsetneqq S,lowbit(S)\in T} F[T]\times {G[S]\over G[T]\times G[S-T]} F[S]=1TS,lowbit(S)TF[T]×G[T]×G[ST]G[S]

总的式子就是 A n s = ∑ S F [ S ] ∗ G [ U ] G [ ∁ U S ] × G [ S ] Ans=\sum\limits_{S}F[S]*{G[U]\over G[\complement _U S]\times G[S]} Ans=SF[S]G[US]×G[S]G[U]

总的时间复杂度为 O ( 3 n ) O(3^n) O(3n)

Code

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 18
#define M 131075
#define mo 998244353
#define LL long long
using namespace std;
LL f[M],g[M],a[N][N],ny[M],a1[N*N][2];
int n,m,cf[N];
LL ksm(LL k,LL n)
{
	LL s=1;
	for(;n;n>>=1,k=k*k%mo) if(n&1) s=s*k%mo;
	return s;
}
int main()
{
	cin>>n>>m;
	fo(i,1,n) fo(j,1,n) a[i][j]=1;
	cf[0]=1;
	fo(i,1,n) cf[i]=cf[i-1]<<1;
	fo(i,1,m)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		a[x][y]=a[y][x]=z;
	}
	g[0]=1;
	fo(i,1,cf[n]-1) 
	{
		g[i]=1;
		fo(x,1,n) fo(y,x+1,n) if((cf[x-1]&i)&&(cf[y-1]&i)) g[i]=g[i]*a[x][y]%mo;
		ny[i]=ksm(g[i],mo-2);
	}
	LL ans=0;
	ny[0]=1;
	fo(i,1,cf[n]-1)
	{
		f[i]=1;
		fod(j,n,1) 
		{
			if(cf[j-1]&i)
			{
				int l=i^cf[j-1];
				for(int r=l&(l-1);r;r=l&(r-1))
				{
					f[i]=(f[i]-f[r^cf[j-1]]*g[i]%mo*ny[r^cf[j-1]]%mo*ny[l^r]%mo+mo)%mo;
				}
				if(l!=0) f[i]=(f[i]-f[cf[j-1]]*g[i]%mo*ny[cf[j-1]]%mo*ny[l]%mo+mo)%mo;
				break;	
			}	
		}
		LL s=f[i]*g[cf[n]-1]%mo*ny[i]%mo*ny[(cf[n]-1)^i]%mo;
		ans=(ans+s)%mo;
	}
	printf("%lld\n",ans);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值