【TC SRM 670】【TC 13891】【BZOJ4671】Gxor(斯特林反演)(线性基)(高斯消元)

传送门


题解:

枚举一下连通块,不同块之间不允许连边,同块之间允许任意连边。

然后高斯消元算一下方案数。

我们需要算的是 1 1 1个连通块的方案数。

考虑一个实际连通块数量为 t t t的图,它会在我们枚举 k k k个连通块的时候被算 S t , k S_{t,k} St,k次。

考虑 ∑ i ( − 1 ) i − 1 S n , i ( i − 1 ) ! = [ n = 1 ] \sum_{i}(-1)^{i-1}S_{n,i}(i-1)!=[n=1] i(1)i1Sn,i(i1)!=[n=1]

于是乘上一个 ( − 1 ) i − 1 ( i − 1 ) ! (-1)^{i-1}(i-1)! (1)i1(i1)!的系数就行了。


代码:

#include
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

struct Gxor{
	private:
		int s,n;
		int g[65][15][15];
		ll ans,b[65],fac[15];
		int bl[15];
		
		void calc(int now){
			memset(b,0,sizeof b);
			for(int re i=0;i<n;++i)
			for(int re j=i+1;j<n;++j)
			if(bl[i]^bl[j]){
				ll t=0;
				for(int re k=0;k<s;++k)t|=(ll)g[k][i][j]<<k;
				for(int re k=49;~k;--k)if(t&(1ll<<k)){
					if(b[k])t^=b[k];
					else {b[k]=t;break;}
				}
			}
			int ct=0;for(int re i=0;i<50;++i)ct+=b[i]>0;
			ans+=(now&1?1ll:-1ll)*(1ll<<s>>ct)*fac[now-1];
		}
		void dfs(int u,int now){
			if(u==n){calc(now);return ;}
			for(int re i=1;i<=now+1;++i)
			bl[u]=i,dfs(u+1,std::max(now,i));
		}
		
	public:
		Gxor(){}
		ll countsubs(std::vector<std::string> S){
			s=S.size();
			for(int re i=0;i<S.size();++i){
				int len=S[i].size(),t=0;
				n=(1+sqrt(1+(len<<3)))/2;
				for(int re u=0;u<n;++u)
				for(int re v=u+1;v<n;++v)
				g[i][u][v]=S[i][t++]-'0';
			}
			fac[0]=1;for(int re i=1;i<=n;++i)fac[i]=fac[i-1]*i;
			dfs(0,0);
			return ans;
		}
};

#ifdef zxyoi

Gxor Solver;

std::vector<std::string> S;

int s;
signed main(){
	std::ios::sync_with_stdio(false);
	std::cin>>s;
	for(int re i=0;i<s;++i){
		S.push_back(std::string(""));
		std::cin>>S.back();
	}
	std::cout<<Solver.countsubs(S);
	return 0;
}

#endif

你可能感兴趣的:(容斥原理,高斯消元,线性基,组合数学)