1. 程式人生 > >[JZOJ 5957] scarborough fair【狀壓DP】【圖論】

[JZOJ 5957] scarborough fair【狀壓DP】【圖論】

Description

給定一個n個點m條邊的無向圖
每條邊有一個不出現的概率(不為0),求連通塊數的期望
無重邊自環
答案對998244353取模
n 17 , m n

( n 1 ) 2 n\leq 17,m\leq {n(n-1)\over 2}

Solution

考慮單獨計算每個連通塊的貢獻,我們可以列舉一個點集,然後令它自成一個連通塊,算出概率,乘上它的所有出邊不出現的概率,這就是這個連通塊對總期望的貢獻,直接累加。

那麼現在的問題就是,給定一個點集S,我們需要求出點集S的匯出子圖(即內部的邊+點)連通的概率。

這就是套路了,我們考慮容斥,用1-不連通的概率和。
F [ S ]

F[S] 為點集S內部連通的概率。
列舉其中標號最小的點i所在的連通塊點集T,要求 T S , i T T\subsetneqq S,i\in T ,這可以採用子集列舉,時間複雜度是 O ( 3 n ) O(3^n)
那麼現在還需要計算的是 T T S T S-T 之間的邊不出現概率之積
如果暴力統計,那麼複雜度要多乘個n

然而有個非常妙的方法。
G [ S ] G[S] 為點集 S S 的匯出子圖中的邊的不出現概率之積
那麼 T T S T S-T 之間的邊不出現概率之積就是 G [ S ] G [ T ] × G [ S T ] {G[S]\over G[T]\times G[S-T]}

因此列舉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]}

總的式子就是 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]}

總的時間複雜度為 O ( 3 n ) O(3^n)

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);
}