码迷,mamicode.com
首页 > 其他好文 > 详细

【题解】bzoj2560串珠子

时间:2018-05-18 22:50:21      阅读:194      评论:0      收藏:0      [点我收藏+]

标签:#define   ++   情况   clu   nbsp   状压   zoj   \n   long   

  挺强的……容斥+状压DP。首先想到如果可以求出f[k],f[k]代表联通状态为k的情况下的合法方案数,则f[k] = g[k] - 非法方案数。g[k]为总的方案数,这是容易求得的。那么非法方案数我们可以枚举 k 的子集 j,则 j 联通而剩下的则随意连(不与j联通)。可是做到这里以为自己做出来的,实际上并没有……

  注意到枚举到子集 j 时,若 s‘ = k - j, 那如果 s‘ 中有一个联通的方案 s‘‘,我们在这里减去一次,在之后枚举到s‘‘时又会枚举到这个方案一次。实际上,这也就是说0111与1000这两个子集是对称的。所以我们为了避免这样的情况,就锁定一个点a,使得点 a 一定不出现在集合 j 中,可以使得 a 只能出现在集合 s‘ 中,也就避免了重复。

  代码有参考,如有雷同,是我抄的 (o′ω`o)?

#include <bits/stdc++.h>
using namespace std;
#define maxn 20
#define maxm ((1 << 16) + 2)
#define mod 1000000007
#define int long long
int n, bin[maxn], a[maxn][maxn];
int f[maxm], g[maxm];

int read()
{
    int x = 0, k = 1;
    char c;
    c = getchar();
    while(c < 0 || c > 9) { if(c == -) k = -1; c = getchar(); }
    while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = getchar();
    return x * k;
}

signed main()
{
    n = read(); bin[0] = 1;
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= n; j ++)
            a[i][j] = read();
    for(int i = 1; i <= n; i ++) bin[i] = bin[i - 1] << 1;
    for(int k = 0; k < bin[n]; ++ k)
    {
        f[k] = 1;
        for(int i = 1; i < n; i ++)
            if(k & bin[i - 1])
                for(int j = i + 1; j <= n; j ++)
                    if(k & bin[j - 1])
                        f[k] = f[k] * (a[i][j] + 1) % mod;
        g[k] = f[k]; int K = (k ^ (k & -k));
        for(int j = K; j; j = (j - 1) & K) 
            f[k] = (f[k] - g[j] * f[k ^ j] % mod + mod) % mod;
    }
    printf("%lld\n", f[bin[n] - 1]);
    return 0;
} 

 

【题解】bzoj2560串珠子

标签:#define   ++   情况   clu   nbsp   状压   zoj   \n   long   

原文地址:https://www.cnblogs.com/twilight-sx/p/9058262.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!