zoukankan      html  css  js  c++  java
  • [BZOJ 4671]异或图

    Description

    题库链接

    给定 (s) 个结点数相同且为 (n) 的图 (G_1sim G_s) ,设 (S = {G_1, G_2,cdots , G_s}) ,问 (S) 有多少个子集的异或为一个连通图。

    (1leq nleq 10,1leq sleq 60)

    Solution

    不妨记 (f_x) 为连通块个数至少(x) 的方案数, (g_x) 为连通块恰好(x) 的方案数。

    容易得到:

    [f_x=sum_{i=x}^negin{Bmatrix}i\xend{Bmatrix}g_i]

    其中第二类斯特林数的含义是将 (i) 个连通块塞成 (x) 个的方案数。至于为什么要塞成 (x) 个,这和 (f) 的计算方式有关,之后会提到。

    那么由斯特林反演

    [g_x=sum_{i=x}^n(-1)^{i-x}egin{bmatrix}i\xend{bmatrix}f_i]

    那么

    [egin{aligned}g_1&=sum_{i=1}^n(-1)^{i-1}egin{bmatrix}i\1end{bmatrix}f_i\&=sum_{i=1}^n(-1)^{i-1}(i-1)!f_iend{aligned}]

    考虑如何求 (f) ,我们可以去枚举子集划分,对于横跨两个集合的边,我们必须让他们异或为 (0) ,集合内的边可以随意连,我们不用管(这样可能会导致集合内不连通,这就是上面第二类斯特林数的含义)。

    这样我们可以用 (O(bell(n))) 的时间枚举子集划分。然后对于每一个划分,用线性基找出边集的极大线性无关组个数,记为 (tot) ,那么在当前集合划分下方案为 (2^{s-tot})

    总复杂度 (O(bell(n)sfrac{n(n-1)}{2}))

    Code

    #include <bits/stdc++.h>
    #define ll long long
    using namespace std;
    
    char ch[120];
    ll t, x, bin[64], fac[64], ans, base[64], mp[64];
    int s, n, belong[64];
    
    void cal(int sz) {
        t = 0; int cnt = 0;
        for (int i = 0; i < 64; i++) base[i] = 0;
        for (int i = 1, l = -1; i <= n; i++)
            for (int j = i+1; j <= n; j++)
                t |= bin[++l]*(belong[i] != belong[j]);
        for (int j = 1; j <= s; j++) {
            x = t&mp[j];
            for (int i = 63; i >= 0; i--)
                if (x&bin[i]) {
                    if (!base[i]) {base[i] = x; ++cnt; break; }
                    else x ^= base[i];
                }
        }
        if (sz&1) ans += fac[sz-1]*bin[s-cnt];
        else ans -= fac[sz-1]*bin[s-cnt];
    }
    void dfs(int x, int sz) {
        if (x > n) {cal(sz); return; }
        for (int i = 1; i <= sz+1; i++)
            belong[x] = i, dfs(x+1, sz+(i == sz+1));
    }
    void work() {
        scanf("%d", &s); bin[0] = fac[0] = 1;
        for (int i = 1; i <= 10; i++) fac[i] = 1ll*fac[i-1]*i;
        for (int i = 1; i < 64; i++) bin[i] = bin[i-1]<<1;
        scanf("%s", ch+1);
        for (int len = strlen(ch+1); n*(n-1)/2 < len; ++n);
        for (int i = 1, t = 0; i <= n; i++)
            for (int j = i+1; j <= n; j++)
                if (ch[++t] == '1') mp[1] |= bin[t-1];
        for (int T = 2; T <= s; T++) {
            scanf("%s", ch+1);
            for (int i = 1, t = 0; i <= n; i++)
                for (int j = i+1; j <= n; j++)
                    if (ch[++t] == '1') mp[T] |= bin[t-1];
        }
        dfs(1, 0); printf("%lld
    ", ans);
    }
    int main() {work(); return 0; }
  • 相关阅读:
    Pycharm简单使用教程
    【Jenkins学习】【第二节】 jenkins构建触发器定时任务
    Docker之从零开始制作docker镜像
    手机APP自动化环境搭建
    格式字符详解
    Bash Shell之内建命令和保留字
    asp.net 实现后台异步处理的方式
    Spring3.2.0 之后各个版本完整包下载地址
    Oracle的rollup、cube、grouping sets函数
    C# 委托类型及使用
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/9281209.html
Copyright © 2011-2022 走看看