[JZOJ 5957] scarborough fair【狀壓DP】【圖論】
阿新 • • 發佈:2018-12-21
Description
給定一個n個點m條邊的無向圖
每條邊有一個不出現的概率(不為0),求連通塊數的期望
無重邊自環
答案對998244353取模
Solution
考慮單獨計算每個連通塊的貢獻,我們可以列舉一個點集,然後令它自成一個連通塊,算出概率,乘上它的所有出邊不出現的概率,這就是這個連通塊對總期望的貢獻,直接累加。
那麼現在的問題就是,給定一個點集S,我們需要求出點集S的匯出子圖(即內部的邊+點)連通的概率。
這就是套路了,我們考慮容斥,用1-不連通的概率和。
設
為點集S內部連通的概率。
列舉其中標號最小的點i所在的連通塊點集T,要求
,這可以採用子集列舉,時間複雜度是
的
那麼現在還需要計算的是
和
之間的邊不出現概率之積
如果暴力統計,那麼複雜度要多乘個n
然而有個非常妙的方法。
令
為點集
的匯出子圖中的邊的不出現概率之積
那麼
和
之間的邊不出現概率之積就是
因此列舉T,就有
總的式子就是
總的時間複雜度為
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);
}