1. 程式人生 > >BZOJ2560串珠子

BZOJ2560串珠子

continue def har tchar turn else digi ret 等於

/*
很清新的一道題(相比上一道題)
g[S]表示該 S集合中胡亂連的所有方案數, f[S] 表示S集合的答案
那麽F[S] 等於G[S]減去不合法的部分方案
不合法的方案就枚舉合法的部分就好了

 g[S]求法可以由選擇一個點和其他沒被選擇的之間連邊的

*/
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#include<cmath>
#define ll long long
#define M 16
#define mmp make_pair
using namespace std;
const int mod = 1000000007;
void add(int &x, int y) {
    x += y;
    x -= x >= mod ? mod : 0;
    x += x < 0 ? mod : 0;
}

int read() {
    int nm = 0, f = 1;
    char c = getchar();
    for(; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    for(; isdigit(c); c = getchar()) nm = nm * 10 + c - '0';
    return nm * f;
}
int poww(int a, int b) {
    int ans = 1, tmp = a;
    for(; b; b >>= 1, tmp = 1ll * tmp * tmp % mod) if(b & 1) ans = 1ll * ans * tmp % mod;
    return ans;
}

int f[1 << M], g[1 << M], a[M][M], n;

int main() {
    n = read();
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < n; j++) {
            a[i][j] = read();
        }
    }
    f[0] = g[0] = 1;
    for(int i = 1; i < (1 << n); i++) {
        int x = -1, ans = 1;
        for(int j = 0; j < n; j++) {
            if((i & (1 << j)) == 0) continue;
            if(x == -1) x = j;
            else {
                ans = 1ll * ans * (a[x][j] + 1) % mod;
            }
        }
        g[i] = 1ll * g[i ^ (1 << x)] * ans % mod;
    }
    for(int i = 1; i < (1 << n); i++) {
        f[i] = g[i];
        int k = i ^ (i & -i);
        for(int j = k; j; j = (j - 1) & k) {
            add(f[i], -1ll * g[j] * f[i ^ j] % mod);
        }
    }
    cout << f[(1 << n) - 1] << "\n";
    return 0;
}

BZOJ2560串珠子