NKOJ4772 串珠子 状压dp

题面:铭铭有n个十分漂亮的珠子和若干根颜色不同的绳子。现在铭铭想用绳子把所有的珠子连接成一个整体。
  现在已知所有珠子互不相同,用整数1到n编号。对于第i个珠子和第j个珠子,可以选择不用绳子连接,或者在ci,j根不同颜色的绳子中选择一根将它们连接。如果把珠子看作点,把绳子看作边,将所有珠子连成一个整体即为所有点构成一个连通图。特别地,珠子不能和自己连接。
  铭铭希望知道总共有多少种不同的方案将所有珠子连成一个整体。由于答案可能很大,因此只需输出答案对1000000007取模的结果。

解法:f[i]是合法方案总数,g[i]是所有方案总数,只需将所有方案总数减去合法方案总数即为答案。

#include <bits/stdc++.h>

using namespace std;

#define ll long long

const ll mod = 1e9 + 7;

ll c[25][25], f[(1 << 16) + 1], g[(1 << 16) + 1];

main()
{
    ll n;
    scanf("%lld", &n);
    for (ll i = 1; i <= n; i++)
    {
        for (ll j = 1; j <= n; j++)
        {
            scanf("%lld", &c[i][j]);
        }
    }
    ll tot = (1 << n) - 1;
    for (ll s = 1; s <= tot; s++)
    {
        g[s] = 1;
        for (ll i = 1; i <= n; i++)
        {
            if (s & (1 << i - 1))
            {
                for (ll j = i + 1; j <= n; j++) if (s & (1 << j - 1)) g[s] = (g[s] * (c[i][j] + 1)) % mod;
            }
        }
    }
    for (ll s = 1; s <= tot; s++)
    {
        for (ll i = s & (s - 1); i; i = s & (i - 1))
        {
            if (!((i ^ s) & (s & -s))) f[s] = (f[s] + f[i] * g[i ^ s]) % mod;
        }
        f[s] = ((g[s] - f[s]) % mod + mod) % mod;
    }
    printf("%lld", f[tot]);
}

 

posted @ 2021-01-05 19:51  Chasing-Dreams  阅读(113)  评论(0)    收藏  举报