【NOIP2018模擬11.7A組】scarborough fair
阿新 • • 發佈:2019-01-12
PROBLEM
求無向圖期望聯通快的個數。
SOLUTION
考慮將每一個聯通塊的貢獻獨立,我們需要得知一個聯通塊內部聯通的概率,與其不與外面任何一個點聯通的概率。
考慮一種經典的做法。我們要求聯通的概率,用1減去不連通的概率。我們設F[S]表示S這個聯通塊聯通的概率。轉移我們列舉編號最小的點所在的子集,設為T,那麼F[S]+=F[T]*e[T][S xor T],e表示T這個子集不向另外節點連邊的概率,也就是T與S^T這兩個集合的邊斷開的期望。
這個東西我們可以求出S每條邊都不連的期望,除以T與S^T每條邊都不連的期望,就可以得到這兩個集合中間的那些邊不相連的期望。
最後列舉集合,再列舉集合的子集,總複雜度O(3^n)
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #define ll long long #define mo 998244353 #define maxn 21 using namespace std; int n,m,Mx,S,S0,T,i,j,k,x,y,cnt; ll ans,bet[1<<maxn],inv[1<<maxn],num[1<<maxn],f[1<<maxn],z,a[maxn][maxn]; int low(int x){return x&-x;} ll ksm(ll x,ll y){ ll s=1; for(;y;y>>=1,x=x*x%mo) if (y&1) (s*=x)%=mo; return s; } int main(){ freopen("fair.in","r",stdin); freopen("fair.out","w",stdout); scanf("%d%d",&n,&m); for(i=0;i<n;i++) num[1<<i]=i; for(i=1;i<=n;i++) for(j=1;j<=n;j++) a[i][j]=-1; for(i=1;i<=m;i++){ scanf("%d%d%lld",&x,&y,&z); a[x][y]=a[y][x]=z; } bet[0]=1,inv[0]=1; for(S=1;S<1<<n;S++){ i=num[low(S)]; bet[S]=bet[S^(1<<i)]; for(j=0;j<n;j++) if (j!=i&&a[i+1][j+1]!=-1&&((S>>j)&1)) (bet[S]*=a[i+1][j+1])%=mo; inv[S]=ksm(bet[S],mo-2); } Mx=(1<<n)-1; for(S=1;S<1<<n;S++) { if (low(S)==S) f[S]=1; else{ S0=S-low(S); for(j=S0;;j=((j-1)&S0)){ T=low(S)+j; (f[S]+=f[T]*bet[S]%mo*inv[S^T]%mo*inv[T]%mo)%=mo; if (j==0) break; } f[S]=(1+mo-f[S])%mo; } (ans+=f[S]*bet[Mx]%mo*inv[Mx^S]%mo*inv[S]%mo)%=mo; } printf("%lld",ans); }