bzoj 2560串珠子(狀壓dp+容斥)


題目鏈接

題意

有$n$個點,任意兩點$i$,$j$之間有$C$$i,j$種連接方式,求將$n$個點連通的方案數。$n<=16$

題解

  • 考慮用所有連接方案數減去不連通的方案數
  • 因為$n<=16$,所以可以狀壓dp
  • 設$g[i]$為點集為$i$時的無向圖個數,$f[i]$為點集為$i$時的無向連通圖的個數.
  • 先預處理出所有$g[i]$的值,顯然$g[i]=\prod_{j \in i, k \in i, j < k}(c[j][k]+1)$
  • $f[i]$就等於$g[i]$減去包含$i$的最低位的$i$的真子集的無向連通圖個數$f[j]$與剩余點集$g[i - j]$的乘積,這樣減就能保證不會重復。
  • 即$f[i]=g[i]-\sum_{j \subsetneqq i,(i\&(-i)) \in j}f[j]*g[i - j]$,時間復雜度為$O(3^n+2^n*n^2)$
  • 查看代碼
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 20;
    const int mod = 1e9+7;
    ll qpow(ll a,ll b){ll res=1;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
    ll a[maxn][maxn];
    ll g[1<<17],f[1<<17];
    int main()
    {
    #ifndef ONLINE_JUDGE
        freopen("simple.in", "r", stdin);
        freopen("simple.out", "w", stdout);
    #endif
        int n;
        scanf("%d",&n);
        int mx = 1<<n;
        for(int i = 0;i < n;++i){
            for(int j = 0;j < n;++j)scanf("%lld",&a[i][j]);
        }
        for(int i = 1;i < mx;++i){
            g[i]=1;
            for(int j = 0;j < n;++j){
                for(int k = 0;k < j;++k){
                    if(((i>>j)&1)&&((i>>k)&1)){
                        g[i]=g[i]*(a[j][k]+1)%mod;
                    }
                }
            }
            ll tmp = 0;
            for(int j = i&(i-1);j;j = i&(j-1)){
                if(j&(i&-i))
                tmp=(f[j]*g[i^j]+tmp)%mod;
            }
            f[i]=(g[i]-tmp+mod)%mod;
        }
        cout<<f[mx-1]<<endl;
        return 0;
    }
    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM