BZOJ4671异或图

打不开爆炸(OJ)就不挂网址了

直接求不好求,考虑容斥

(f_i)表示有(i)个联通块的答案,(g_i)表示有(i)部分不与外界联通的答案

有方程

[g_i=sumlimits_{j=i}^{n}egin{Bmatrix}j\iend{Bmatrix}f_j ]

根据斯特林反演有

[f_i=sumlimits_{j=i}^{n}(-1)^{j-i}egin{bmatrix}j\iend{bmatrix}g_j ]

最终答案是

[f_1=sumlimits_{j=1}^{n}(-1)^{j-1}egin{bmatrix}j\1end{bmatrix}g_j ]

[f_1=sumlimits_{j=1}^{n}(-1)^{j-1}(j-1)!g_j ]

点数(le 10)所以考虑直接大力枚举子集划分

我们强制不同的集合之间边不存在,然后将不同图的集合之间连边情况转为二进制串插入线性基

设集合数量为(t),那么最后(g_t)的数量是不同图之间异或等于(0)的方案数,假设插入线性基中的数量为(c),那么答案为(2^{s-c})

#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define ls(p) (p<<1)
#define rs(p) (p<<1|1)
#define lowbit(i) ((i)&(-i))
	inline int read()
	{
		int x=0;char ch,f=1;
		for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
		if(ch=='-') f=0,ch=getchar();
		while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
		return f?x:-x;
	}
	const int N=110;
	int n,num,ans,sum;
	int inv[N],fac[N],ifac[N];
	char s[N];
	int g[N][N][N],a[N];
	int p[N];
	inline void dfs(int x,int up)
	{
		if(x==num)
		{
			memset(p,0,sizeof(p));
			int sum=0;
			for(int i=1;i<=n;++i)
			{
				int ret=0,tot=0;
				for(int j=1;j<=num;++j)
				{
					for(int k=j+1;k<=num;++k)
					{
						if(a[j]!=a[k]) ret|=(g[i][j][k]<<tot);
						++tot;
					}
				}
				for(int j=tot;~j;--j) if(ret>>j&1)
				{
					if(!p[j])
					{
						++sum;p[j]=ret;
						break;
					}
					ret^=p[j];
				}
			}
			if(up&1) ans+=fac[up-1]*(1ll<<(n-sum));
			else ans-=fac[up-1]*(1ll<<(n-sum));
			return;
		}
		for(int i=1;i<=up+1;++i)
		{
			a[x+1]=i;
			dfs(x+1,max(i,up));
		}
	}
	inline void main()
	{
		n=read();
		for(int i=fac[0]=1;i<12;++i) fac[i]=fac[i-1]*i;
		for(int len,cnt,i=1;i<=n;++i)
		{
			scanf("%s",s+1);
			len=strlen(s+1),cnt=0;
			for(int j=1;!num;++j)
				if(j*(j-1)==2*len) num=j;
			for(int j=1;j<=num;++j)
				for(int k=j+1;k<=num;++k)
					g[i][j][k]=s[++cnt]-'0';
		}
		a[1]=1;
		dfs(1,1);
		printf("%lld
",ans);
	}
}
signed main()
{
	red::main();
	return 0;
}
原文地址:https://www.cnblogs.com/knife-rose/p/13056196.html