題解-bzoj2560 串珠子
阿新 • • 發佈:2018-12-20
剛被教練數落了一通,心情不好,來寫篇題解
Problem
題目簡述:給定\(n\)個點的,每兩個點\(i,j\)之間有\(c_{i,j}\)條直接相連的路(其中只能選一條或不選),問共有多少種方案可以使得整張圖連通。\(n\leq 16\)
Solution
算是遇到的沒那麼套路的容斥題了 雖然還是有點套路
發現\(n\leq 16\)各種暗示我們要狀壓,於是按照以往狀壓的題的套路,設\(f(S)\)表示當\(S\)集合中的點連通方案數
發現不是很好直接計算,但總方案數又很好得出,於是考慮容斥,設\(g(S)\)表示集合\(S\)中的點之間隨意相連的方案數
根據定義可得
\[g(S)=\prod_{i,j\in S}(c_{i,j}+1)\]
想法用\(g\)去消掉\(f\)不滿足題意的方案數,聯想到城市規劃中的做法:限定\(1\)號節點的連通集合大小
類似的,這裡可以限定\(S\)中編號最小的點連通大小(當然編號最大的點也行)
列舉\(S\)中編號最小的點連通塊大小,可以得到(設\(H\)為集合\(S\)中去除最小元素的集合):
\(f(S)=g(S)-\sum_{T\subseteq H}g(T)f(S-T)\)
題目之間類比關係好多啊,比如上一篇就是二項堆和AC自動機的類比
Code
#include <cstdio> const int N=18,M=1<<N,p=1e9+7; int g[M],f[M],bin[N]; int a[N][N],n; inline int qm(int x){return x<p?x:x-p;} int main(){ scanf("%d",&n); for(int i=0;i<n;++i) for(int j=0;j<n;++j) scanf("%d",&a[i][j]); bin[0]=1; for(int i=1;i<=n;++i)bin[i]=bin[i-1]<<1; for(int S=0,s;S<bin[n];++S){ f[S]=1; for(int i=0;i<n;++i)if(bin[i]&S) for(int j=i+1;j<n;++j)if(bin[j]&S) f[S]=1ll*f[S]*(a[i][j]+1)%p; g[S]=f[S],s=(S-1)&S; for(int i=s;i;i=(i-1)&s) f[S]=qm((int)f[S]-1ll*g[i]*f[S^i]%p+p); } printf("%d\n",f[bin[n]-1]); return 0; }