• BZOJ4671 异或图(容斥+线性基)


    题意

    定义两个结点数相同的图 (G_1) 与图 (G_2) 的异或为一个新的图 (G) ,其中如果 ((u, v))(G_1)(G_2) 中的出现次数之和为 (1) , 那么边 ((u, v))(G) 中, 否则这条边不在 (G) 中.

    现在给定 (s) 个结点数相同的图 (G_{1...s}) , 设 (S = {G_1, G_2, cdots , G_s}) , 请问 (S) 有多少个子集的异或为一个连通图?

    (n le 10, s le 60)

    题解

    原来听过这题,但一直没有想去写,又讲了一遍,就来做了下,可是还不会。。。

    连通图计数的一个经典思路就是容斥。

    对于这道题,我们先用贝尔数 (bell(n)) 的时间来枚举 (n) 个点的子集(联通块)划分,强制连通性 至少 是这个划分。

    也就是说,不同子集的两个点之间一定没有边,相同子集的两个点则任意。

    对于一个有 (m) 个连通块的图。令 (f_i) 为至少有 (i) 个联通块的容斥系数需要满足

    [sum_{i = 1}^{m} {mrace i} f_i = [m = 1] ]

    可以斯特林反演,也可以打表找规律得出

    [f_i = (-1)^{i - 1} (i - 1)! ]

    那么问题就转化成,我们只考虑不同子集中的边。对于 (s) 个边集,有多少种异或方案使得异或和为 (0)

    这个显然是可以利用线性基得到异或方案的,记线性基的元素个数为 (tot) ,由于之中的元素是线性无关的,其他的 (2^{s - tot}) 个集合是一定可以通过异或(或不异或)线性基里的某些元素得到 (0) 的。

    那么方案数其实就是 (2^{s - tot})

    因为此题卡常,所以要卡一些常数才能通过此题qwq 具体可以看代码实现优化。

    代码

    #include <bits/stdc++.h>
    
    #define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
    #define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
    #define Set(a, v) memset(a, v, sizeof(a))
    #define Cpy(a, b) memcpy(a, b, sizeof(a))
    #define debug(x) cout << #x << ": " << (x) << endl
    
    using namespace std;
    
    template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
    template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }
    
    inline int read() {
    	int x(0), sgn(1); char ch(getchar());
    	for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
    	for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
    	return x * sgn;
    }
    
    void File() {
    #ifdef zjp_shadow
    	freopen ("4671.in", "r", stdin);
    	freopen ("4671.out", "w", stdout);
    #endif
    }
    
    typedef long long ll;
    
    const int N = 12, M = 62;
    
    int Strl[N][N], id[N][N], n, s, fac[N], coef[N];
    
    bitset<N> E[M][N]; char str[M];
    
    ll base[M], ans; int bel[N];
    
    pair<int, int> ins[M];
    
    void Dfs(int u, int tot) {
    	if (u > n) {
    		Set(base, 0);
    		int res = 0, tmp = -1;
    		For (i, 1, n) For (j, i + 1, n)
    			if (bel[i] != bel[j]) {
    				ins[++ tmp] = make_pair(i, j);
    			}
    		For (i, 1, s) {
    			ll now = 0;
    			For (j, 0, tmp) if (E[i][ins[j].first][ins[j].second]) now |= 1ll << j;
    			Fordown (j, tmp, 0) if (now >> j & 1) {
    				if (base[j]) now ^= base[j];
    				else { base[j] = now; ++ res; break; }
    			}
    		}
    		ans += coef[tot] * (1ll << (s - res));
    		return;
    	}
    	For (i, 1, tot + 1)
    		bel[u] = i, Dfs(u + 1, max(tot, i));
    }
    
    int main () {
    
    	File();
    
    	s = read();
    	scanf ("%s", str + 1);
    
    	int len = strlen(str + 1);
    
    	while (n * (n - 1) / 2 < len) ++ n;
    
    	For (k, 1, s) {
    		len = 0;
    		For (i, 1, n) For (j, i + 1, n)
    			E[k][i][j] = str[id[i][j] = ++ len] - '0';
    		if (k < s) scanf ("%s", str + 1);
    	}
    
    	fac[0] = 1;
    	For (i, 1, n) {
    		fac[i] = 1ll * fac[i - 1] * i; 
    		coef[i] = (i & 1 ? 1 : -1) * fac[i - 1];
    	}
    	Dfs(1, 0);
    
    	printf ("%lld
    ", ans);
    
    	return 0;
    
    }
    
  • 相关阅读:
    hdu6761 Mininum Index // lyndon分解 + duval贪心 + 秦九韶算法
    hdu6762 Mow // 半平面交 模拟 双端队列
    数据库增删改查操作
    移动端自动化概念
    范围查询和模糊查询
    软件测试技能要求总结
    继承
    luogu_P2024 [NOI2001]食物链
    luogu_P4092 [HEOI2016/TJOI2016]树
    luogu_P2887 [USACO07NOV]防晒霜Sunscreen
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/10246734.html
Copyright © 2020-2023  润新知