zoukankan      html  css  js  c++  java
  • @bzoj


    @description@

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

    现在给定 s 个结点数相同的图 G1...s, 设 S = {G1, G2, . . . , Gs}, 请问 S 有多少个子集的异或为一个连通图?

    原题传送门。

    @solution@

    记 f[i] 表示得到恰好 i 个连通块的方案数,再记 g[i] 表示至少 i 个连通块的方案数。则:

    [g[i] = sum_{j=i}^{n}{jrace i}f[j] ]

    这是很显然的。然后我们反演一下:

    [f[i] = sum_{j=i}^{n}(-1)^{j-i}{jrack i}g[j] ]

    最后要求连通,所以实际上求 f[1] 的值。然后我们又知道 ({nrack 1} = (n - 1)!),所以:

    [ans = f[1] = sum_{i=1}^{n}(-1)^{i-1}(i-1)!g[i] ]

    考虑怎么求 g。我们可以搜索 n 个点哪些点可能在同一个块内(即将 n 个球染色),搜索量为第 n 个贝尔数。
    提出不可能在同一块内的点对之间的边,把 s 个图在这些边上作线性基。然后是经典的线性基计数。

    @accepted code@

    #include <bitset>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    typedef long long ll;
    typedef unsigned long long ull;
    
    int s, n, m;
    bool tag[60]; ull b[60]; int siz;
    void insert(ull x) {
    	for(int i=m-1;i>=0;i--) {
    		if( !tag[m-i-1] || !((x >> i) & 1) ) continue;
    		if( b[i] == 0 ) {
    			b[i] = x, siz++;
    			return ;
    		}
    		else x ^= b[i];
    	}
    }
    
    int clr[10]; ll pw2[65]; ull a[60];
    ll solve() {
    	int p = 0; siz = 0;
    	for(int i=0;i<m;i++) b[i] = 0;
    	for(int i=0;i<n;i++)
    		for(int j=i+1;j<n;j++)
    			tag[p++] = (clr[i] != clr[j]);
    	for(int i=0;i<s;i++)
    		insert(a[i]);
    	return pw2[s - siz];
    }
    ll ans[15];
    void dfs(int d, int l) {
    	if( d == n ) {
    		ans[l + 1] += solve();
    		return ;
    	}
    	dfs(d + 1, clr[d] = l + 1);
    	for(int i=0;i<=l;i++)
    		clr[d] = i, dfs(d + 1, l);
    }
    char str[60];
    int main() {
    	pw2[0] = 1; for(int i=1;i<=60;i++) pw2[i] = 2*pw2[i-1];
    	scanf("%d", &s);
    	for(int i=0;i<s;i++) {
    		scanf("%s", str), m = strlen(str);
    		for(int j=0;j<m;j++)
    			a[i] = (a[i] << 1 | (str[j] - '0'));
    	}
    	for(int i=2;i<=10;i++)
    		if( i*(i - 1)/2 == m ) n = i;
    	dfs(0, -1); ll res = 0, p = 1;
    	for(int i=1;i<=n;p*=i,i++)
    		res = res + (i & 1 ? ans[i]*p : -ans[i]*p);
    	printf("%lld
    ", res);
    }
    

    @details@

    用 unsigned long long 存储会加速很多。

  • 相关阅读:
    time fly
    小论文初稿终于完成
    leetcode之Length of Last Word
    static关键字
    参数传递
    this关键字
    面向对象有三大特征
    空指针异常
    变量按数据类型分为
    构造方法
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12513410.html
Copyright © 2011-2022 走看看