传送门
题解:
枚举一下连通块,不同块之间不允许连边,同块之间允许任意连边。
然后高斯消元算一下方案数。
我们需要算的是 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)i−1Sn,i(i−1)!=[n=1]
于是乘上一个 ( − 1 ) i − 1 ( i − 1 ) ! (-1)^{i-1}(i-1)! (−1)i−1(i−1)!的系数就行了。
代码:
#include<bits/stdc++.h>
#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