直接算不好算,考虑先算出超集和的答案,最后高维后缀差分求出原本的答案。
发现超集和的意义下的答案满足:将 (01) 串的连边关系构成图后,会得到若干条链,若两个 (01) 串得到的链长的可重集相同,则这两个 (01) 串的答案相等。因为答案是由全排列贡献的,所以有这个性质。
考虑直接枚举链长的可重集,也就是直接枚举 (n) 的整数拆分,(18) 的整数拆分只有 (P(18)=385),可以接受。可以通过 (DP) 求出 (g_{i,S}) 表示长度为 (i) 的链,链中的点为 (S) 的方案数。(dfs) 过程中用枚举到对应的链长后进行子集卷积即可,因为最后只用取全集的答案,所以这里可以用集合并卷积来代替子集卷积。
总复杂度为 (O((n^2+P(n))2^n))。
#include<bits/stdc++.h>
#define maxn 20
#define maxm 262154
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,all;
int cnt[maxm];
ll f[maxm],g[maxn][maxm],h[maxn][maxm],v[maxn][maxm];
char w[maxn][maxn];
vector<int> ve;
map<vector<int>,ll> mp;
void dfs(int x,int pre,int sum)
{
if(sum==n)
{
ll val=0;
for(int i=0;i<all;++i) val+=v[x][i]*(cnt[i^(all-1)]&1?-1:1);
mp[ve]=val;
return;
}
for(int i=pre;i+sum<=n;++i)
{
for(int j=0;j<all;++j) v[x+1][j]=v[x][j]*g[i][j];
ve.push_back(i),dfs(x+1,i,i+sum),ve.pop_back();
}
}
int main()
{
read(n),all=1<<n;
for(int i=1;i<all;++i) cnt[i]=cnt[i>>1]+(i&1);
for(int i=0;i<n;++i) scanf("%s",w[i]),h[i][1<<i]=1;
for(int i=0;i<all;++i)
for(int j=0;j<n;++j)
for(int k=0;k<n;++k)
if(!((i>>k)&1)&&w[j][k]=='1')
h[k][i|(1<<k)]+=h[j][i];
for(int i=0;i<all;++i)
for(int j=0;j<n;++j)
g[cnt[i]][i]+=h[j][i];
for(int x=1;x<=n;++x)
for(int len=1;len<all;len<<=1)
for(int i=0;i<all;i+=len<<1)
for(int j=i;j<i+len;++j)
g[x][j+len]+=g[x][j];
for(int i=0;i<all;++i) v[0][i]=1;
dfs(0,1,0),all>>=1;
for(int s=0;s<all;++s)
{
ve.clear();
int pos;
for(int i=0;i<n;i=pos+1)
{
pos=i;
while(pos<n-1&&(s>>pos)&1) pos++;
ve.push_back(pos-i+1);
}
sort(ve.begin(),ve.end()),f[s]=mp[ve];
}
for(int len=1;len<all;len<<=1)
for(int i=0;i<all;i+=len<<1)
for(int j=i;j<i+len;++j)
f[j]-=f[j+len];
for(int i=0;i<all;++i) printf("%lld ",f[i]);
return 0;
}