【BZOJ4671】异或图(容斥原理,线性基)

Description

定义两个结点数相同的图 G1 与图 G2 的异或为一个新的图 G, 其中如果 (u, v) 在 G1 与G2 中的出现次数之和为 1, 那么边 (u, v) 在 G 中, 否则这条边不在 G 中.
现在给定 s 个结点数相同的图 G1…s, 设 S = {G1, G2, … , Gs}, 请问 S 有多少个子集的异或为一个连通图?


Solution

考虑枚举每一种点集的划分方式,我们可以用线性基方便地算出点集之间一定没有边的方案数,但是这样不能保证点集内部一定联通,所以就需要容斥一下。
m m 为枚举的点集的个数,则容斥系数 fi f i 满足:

i=1m{mi}fi=[m=1] ∑ i = 1 m { m i } f i = [ m = 1 ]

由斯特林反演有:
i=1m[i=1](1)mi[mi]=fm ∑ i = 1 m [ i = 1 ] ( − 1 ) m − i [ m i ] = f m

即:
fm=(1)m1(m1)! f m = ( − 1 ) m − 1 ( m − 1 ) !


Code

/************************************************
 * Au: Hany01
 * Date: Jun 9th, 2018
 * Prob: [BZOJ4671] 异或图
 * Email: [email protected]
************************************************/

#include

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
#define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout)
#define rep(i, j) for (register int i = 0, i##_end_ = (j); i < i##_end_; ++ i)
#define For(i, j, k) for (register int i = (j), i##_end_ = (k); i <= i##_end_; ++ i)
#define Fordown(i, j, k) for (register int i = (j), i##_end_ = (k); i >= i##_end_; -- i)
#define Set(a, b) memset(a, b, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define x first
#define y second
#define pb(a) push_back(a)
#define mp(a, b) make_pair(a, b)
#define ALL(a) (a).begin(), (a).end()
#define SZ(a) ((int)(a).size())
#define INF (0x3f3f3f3f)
#define INF1 (2139062143)
#define Mod (1000000007)
#define debug(...) fprintf(stderr, __VA_ARGS__)
#define y1 wozenmezhemecaia

template <typename T> inline bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }
template <typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }

inline int read()
{
    register int _, __; register char c_;
    for (_ = 0, __ = 1, c_ = getchar(); c_ < '0' || c_ > '9'; c_ = getchar()) if (c_ == '-') __ = -1;
    for ( ; c_ >= '0' && c_ <= '9'; c_ = getchar()) _ = (_ << 1) + (_ << 3) + (c_ ^ 48);
    return _ * __;
}

const int maxs = 61, maxn = 11;

struct Edge
{
    int u, v;
    Edge(int u = 0, int v = 0): u(u), v(v) {}
}E[maxn * maxn >> 1];

int s, len, n, G[maxs][maxn][maxn], id[maxn];
LL f[maxn], base[maxn * maxn], Ans;
char str[maxs][maxn * maxn];

void DFS(int cur, int cnt)
{
    if (cur > n) {
        Set(base, 0);
        int tot = 0, sum = 0;
        For(i, 1, n) For(j, i + 1, n) if (id[i] != id[j]) E[++ tot] = Edge(i, j);
        For(i, 1, s) {
            LL val = 0;
            For(j, 1, tot) if (G[i][E[j].u][E[j].v]) val |= (1ll << j);
            Fordown(j, tot, 1) if (val >> j & 1ll)
                if (!base[j]) { base[j] = val, ++ sum; break; }
                else val ^= base[j];
        }
        Ans += f[cnt] * (1ll << (s - sum));
    } else For(i, 1, cnt + 1) id[cur] = i, DFS(cur + 1, max(cnt, i));
}

int main()
{
#ifdef hany01
    File("bzoj4671");
#endif

    s = read();
    For(i, 1, s) scanf("%s", str[i]);
    len = strlen(str[1]);
    n = int(sqrt(len << 1)) + 1;
    For(k, 1, s) {
        int cnt = 0;
        For(i, 1, n) For(j, i + 1, n) G[k][i][j] = str[k][cnt ++] ^ 48;
    }
    f[1] = 1;
    For(i, 2, n) f[i] = f[i - 1] * (LL)(1 - i);

    DFS(1, 0);
    printf("%lld\n", Ans);

    return 0;
}
//思悠悠,恨悠悠,恨到归时方始休。
//    -- 白居易《长相思·汴水流》

你可能感兴趣的:(容斥原理,BZOJ,斯特林反演,线性基)