[BZOJ4671][斯特林反演][高斯消元]异或图

BZOJ4671

首先枚举连通性,强制不同的连通块之间不能有边,这个很好表示,而相同连通块内边的情况不好表示,因为涉及到很多情况和很多限制,而不同连通块之间的情况就只有一个:任意两点间都不能有边
然后就定下了有一些边的出现次数必须是偶数这一条件,高斯消元算一下自由元个数就知道满足上述条件的情况的方案数了
然后考虑一个连通块内部,有可能是几个小连通块,那么从全局的角度考虑,一个n个连通块的图,会在我们计算k个连通块的方案的时候被计算 S n , k S_{n,k} Sn,k次,即把n个连通块分入 k k k个集合
然后由斯特林反演得到:
∑ i = 1 n ( − 1 ) i − 1 S n , i ( i − 1 ) ! = [ n = 1 ] \sum_{i=1}^n(-1)^{i-1}S_{n,i}(i-1)!=[n=1] i=1n(1)i1Sn,i(i1)!=[n=1]
所以1个连通块的方案就是任意个数连通块的方案加起来,而k个连通块的方案又要除以 S n , k S_{n,k} Sn,k,所以k个连通块的贡献就要乘上系数 ( − 1 ) k − 1 ( k − 1 ) ! (-1)^{k-1}(k-1)! (1)k1(k1)!
然后就做完了

Code:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
#define gc getchar
inline int read(){
	int res=0,f=1;char ch=gc();
	while(!isdigit(ch)) {if(ch=='-') f=-f;ch=gc();}
	while(isdigit(ch)) {res=(res+(res<<2)<<1)+(ch^48);ch=gc();}
	return res*f;
}
inline void file(){freopen("lx2.in","r",stdin);freopen("lx.out","w",stdout);}
const int N=105;
int xr[N][N][N];
int id[N];
ll ans,A[N],fac[N];
int s,n;
char scan[N];
inline void init(int n){
	fac[0]=1;
	for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i;
}
inline void gauss(){
	memset(A,0,sizeof(A));
	for(int i=0;i<n;i++)
		for(int j=i+1;j<n;j++) if(id[i]!=id[j]){
			ll tmp=0;
			for(int k=0;k<s;k++) tmp|=(ll)xr[k][i][j]<<k;
			for(int k=49;~k;k--) if(tmp>>k&1){
				if(A[k]) tmp^=A[k];
				else {A[k]=tmp;break;}
			}
		}
}
inline void calc(int m){
	int c=0;
	for(int k=0;k<50;k++) c+=A[k]>0;
	ans+=((m&1)?1ll:-1ll)*(1ll<<s>>c)*fac[m-1];
}
void dfs(int v,int m){
	if(v==n){gauss();calc(m);return;}
	for(int i=1;i<=m+1;i++) id[v]=i,dfs(v+1,max(m,i));
}
int main(){
	s=read();
	for(int i=0;i<s;i++){
        scanf("%s",scan);
        int len=strlen(scan),t=0;
        n=(1+sqrt(1+(len<<3)))/2;
        for(int x=0;x<n;x++)
        	for(int y=x+1;y<n;y++) xr[i][x][y]=scan[t++]-'0';
	}
	init(n);
    dfs(0,0);
    cout<<ans;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值