zoukankan      html  css  js  c++  java
  • 【bzoj4671】异或图(容斥+斯特林反演+线性基)

    传送门

    题意:
    给出(s,sleq 60)张图,每张图都有(n,nleq 10)个点。
    现在问有多少个图的子集,满足这些图的边“异或”起来后,这张图为连通图。

    思路:

    • 直接考虑判断图的连通不好判断,所以考虑枚举连通块来进行容斥。

    • 定义(f_i)表示有(i)个连通块的答案,发现连通块这个东西也不好处理,我们只能处理出有多少个连通块,但无法确定每个连通块内部的连通关系。

    • 定义(g_i)为至少有(i)个连通块的方案数,那么就有关系式:(displaystyle g_i=sum_{j=i}^n egin{Bmatrix} j \ i end{Bmatrix}f_j)。至于为什么要乘以一个第二类斯特林数,相当于我们将单个的连通块再进行组合,有不同的组合方式。

    • 此时(g_i)就相当于有(i)个连通块,内部可连通可不连通的方案数。

    • 因为最终所求为(f_1),通过斯特林反演可得:(displaystyle f_1=sum_{i=1}^n(-1)^{i-1}egin{bmatrix} i \ 1 end{bmatrix}g_i)。那么现在只需要算(g_i)即可。

    • 因为点数很少,可以直接枚举子集划分,我们只需要保证最后不同的集合之间没有边相连即可。

    • 当确定了一种子集划分过后,将连接不同集合的边拿出来,将每张图这类边转为二进制插入线性基中,最后的基为(c)个,那么答案就为(2^{s-c})

    • 将答案累加入(f_i)即可。

    这个题大概就这样做完了,但还有一些小细节,其中,斯特林反演的时候和常见形式稍有不同,但可以通过反转公式证明:

    [left{ egin{aligned} &sum_{k=m}^n(-1)^{n-k}egin{bmatrix} n \ k end{bmatrix}egin{Bmatrix} k \ m end{Bmatrix}=[n=m]\ &sum_{k=m}^n(-1)^{k-m}egin{Bmatrix} n \ k end{Bmatrix}egin{bmatrix} k \ m end{bmatrix}=[n=m] end{aligned} ight. ]

    最后的答案为(2^{s-c})的原因在于,我们相当于来求若干个数异或起来为(0)的方案数,每个图用(x_i)来表示其最终状态,那么如果有(c)个基,说明就有(s-c)个自由变量,对于任意一组自由变量的取值,我们都能找到一组唯一的对应的解满足方程。
    其实这个本质上就是求解一个异或方程组。
    代码如下:

    /*
     * Author:  heyuhhh
     * Created Time:  2019/12/17 15:36:21
     */
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #include <cmath>
    #include <set>
    #include <map>
    #include <queue>
    #include <iomanip>
    #define MP make_pair
    #define fi first
    #define se second
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    #define Local
    #ifdef Local
      #define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
      void err() { std::cout << '
    '; }
      template<typename T, typename...Args>
      void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
    #else
      #define dbg(...)
    #endif
    void pt() {std::cout << '
    '; }
    template<typename T, typename...Args>
    void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 105;
    
    int s, n;
    char ch[N];
    int G[N][N][N];
    int a[N];
    ll p[64], fac[N];
    ll ans;
    
    void dfs(int x, int up) {
        if(x == n) {
            memset(p, 0, sizeof(p));
            int c = 0;
            for(int k = 1; k <= s; k++) {
                ll res = 0;
                int tot = 0;
                for(int i = 1; i <= n; i++) {
                    for(int j = i + 1; j <= n; j++) {
                        if(a[i] != a[j]) res |= ((ll)G[k][i][j] << tot);
                        ++tot;
                    }
                }   
                for(int i = tot; i >= 0; i--) if(res >> i & 1) {   
                    if(!p[i]) {
                        ++c; p[i] = res; 
                        break;   
                    }
                    res ^= p[i];
                }
            }
            if(up & 1) ans += fac[up - 1] * (1ll << (s - c));
            else ans -= fac[up - 1] * (1ll << (s - c));
            return;
        }
        for(int i = 1; i <= up + 1; i++) {
            a[x + 1] = i; dfs(x + 1, max(i, up));   
        }
    }
    
    void run(){
        fac[0] = 1;
        for(int i = 1; i < 12; i++) fac[i] = fac[i - 1] * i;
        for(int k = 1; k <= s; k++) {
            cin >> (ch + 1);
            int len = strlen(ch + 1), cnt = 0;
            for(int j = 1; !n; j++) if(j * (j - 1) == 2 * len) n = j;
            for(int i = 1; i <= n; i++) {
                for(int j = i + 1; j <= n; j++) {
                    G[k][i][j] = ch[++cnt] - '0';
                }
            }
        }
        a[1] = 1;
        dfs(1, 1);
        cout << ans << '
    ';
    }
    
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        while(cin >> s) run();
        return 0;
    }
    
  • 相关阅读:
    tomcat 添加用户名和密码
    linux系统下获取cpu、硬盘、内存使用率
    snmp 企业对应的mib编号
    String加密解密 2017.07.26
    Mongo日期
    linux sed 批量替换多个文件中的字符串
    Python和giL的关系
    vim
    乌班图
    Python
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/12056370.html
Copyright © 2011-2022 走看看