bzoj4671 异或图 [搜索+线性基+斯特林反演]

2 篇文章 0 订阅
1 篇文章 0 订阅
该博客探讨了如何计算由多个异或图组成的集合中,有多少个子集的异或结果形成一个连通图。通过枚举和容斥原理,结合线性基计算自由元以及斯特林反演公式,求解至少含有i个连通块的子集方案数。最终提供了一种求解f(i)和g(i)的方法,用于高效解决此类问题。
摘要由CSDN通过智能技术生成

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


Solution:
直接枚举不可做,那么考虑容斥,计算至少有几个连通块进行容斥,设方案数为fi。那么结果就是恰好有i个连通块,那么就是 gi g i
那么花费 bell(n) b e l l ( n ) 的时间划分子集,这些子集之间不能有边,利用线性基算出自由元数量 x x ,那么对应的方案数就是2x
考虑 gi g i fi f i 的关系,有
fm=ni=mgiS(i,m) f m = ∑ i = m n g i ∗ S ( i , m )
根据斯特林反演
gm=ni=m(1)ims(i,m)fi g m = ∑ i = m n ( − 1 ) i − m ∗ s ( i , m ) ∗ f i
那么得出 g1=ni=1(1)i1(i1)!fi g 1 = ∑ i = 1 n ( − 1 ) i − 1 ∗ ( i − 1 ) ! ∗ f i
所以每次计算出 fi f i ,然后套入式子即可。


#include <bits/stdc++.h>
using namespace std;
const int maxn = 11, maxm = 65;
int n, m;
long long ans;
int g[maxm][maxn][maxn], bl[maxn];
long long a[maxm], fac[maxn];
char s[maxm];
void dfs(int dep, int k) {
    if(dep == n) {
        memset(a, 0, sizeof(a));
        for(int i = 1; i <= n; ++i) {
            for(int j = i + 1; j <= n; ++j) {
                if(bl[i] != bl[j]) {
                    long long tmp = 0;
                    for(int u = 1; u <= m; ++u) {
                        tmp |= 1LL * g[u][i][j] << (u - 1);
                    }
                    for(int t = m - 1; ~t; --t) {
                        if(tmp >> t & 1) {
                            if(a[t]) {
                                tmp ^= a[t];
                            } else {
                                a[t] = tmp;
                                break;
                            }
                        }
                    }   
                }   
            }
        }
        int c = 0;
        for(int i = 0; i < m; ++i) {
            c += a[i] > 0; 
        }
        ans += ((k & 1) ? 1 : -1) * (1LL << (m - c)) * fac[k - 1];  
        return;
    }
    for(int i = 1; i <= k + 1; ++i) {
        bl[dep + 1] = i;
        dfs(dep + 1, max(k, i));
    }
}
int main() {
    scanf("%d", &m);
    for(int i = 1; i <= m; ++i) {
        scanf("%s", s + 1);
        int l = strlen(s + 1), t = 0;
        n = (1 + sqrt(1 + (l << 3))) / 2;
        for(int u = 1; u <= n; ++u) {
            for(int v = u + 1; v <= n; ++v) {
                g[i][u][v] = s[++t] - '0';  
            }
        }
    }
    fac[0] = 1;
    for(int i = 1; i <= n; ++i) {
        fac[i] = fac[i - 1] * i;
    }
    dfs(0, 0);
    printf("%lld\n", ans);
    return 0;
} 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值