题解 CF1326F2 Wise Men (Hard Version)
状压神仙题,对于数数神仙可能偏套路但是我不是
主要思路
首先可以想到,最后选择点的顺序在题中给的邻接矩阵上会形成多条链,自然想到压完状态后能不能直接对每条链的方案数进行 dp 呢
答案是不能,因为这样做不能保证两条链能不能合并成一条链(即前面的链的末尾与后面的链的开头有边,这显然不能简单利用乘法原理处理),于是自然想到容斥
设
注意到此时对于每条链的情况分开计算是合法的,于是开始 dp
\text{Part.1} 预处理
先考虑对于每条链的方案数进行 dp,设长度为
显然有一个
\text{Part.2} 计算F
接下来我们发现,对于链长度集合完全相同的生成的二进制数,因为只是链交换顺序,所以其方案数是相同的
这告诉我们只需要枚举
设
那么接下来考虑计算
实际上是一个子集卷积,但是做
设
考虑记录
可以提前使用高维前缀和
然后你大力爆搜一发会发现
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define int LL
#define db double
#define INF 2147483647
#define LLINF 9223372036854775807
#define LL long long
using namespace std;
int inline read(){
int num=0,neg=1;char c=getchar();
while(!isdigit(c)){if(c=='-')neg=-1;c=getchar();}
while(isdigit(c)){num=(num<<3)+(num<<1)+c-'0';c=getchar();}
return num*neg;
}
const int maxn=19,r=998244353,mod=19260817;
LL n,a[maxn][maxn],b,f[maxn][1<<maxn],cnt[1<<maxn];
LL g[1<<maxn],p[maxn][1<<maxn],h[1<<maxn],F[1<<maxn];char str[maxn];
LL c[maxn],sta[maxn];LL res[mod];
void dfs(int x,int sum,int num){
if(sum==n){
int P=1,ans=0,s=0;
for(int i=1;i<=num;i++)ans=(ans+1ll*c[i]*P%mod)%mod,P=1ll*P*r%mod;
for(int i=1;i<(1<<n);i++){
h[i]=1;
for(int j=1;j<=num;j++)
h[i]=h[i]*p[c[j]][i];
}
for(int i=1;i<(1<<n);i++)s+=((n-cnt[i])&1)?-h[i]:h[i];res[ans]=s;return;
}for(int i=x;i<=n-sum;i++)c[num+1]=i,dfs(i,sum+i,num+1);
}
void FWTor(LL *A,int len,int opt){
for(int i=1;i<(1<<len);i<<=1)
for(int j=0;j<(1<<len);j+=(i<<1))
for(int k=0;k<i;k++)
A[i+j+k]+=A[j+k]*opt;
}
void FWTand(LL *A,int len,int opt){
for(int i=1;i<(1<<len);i<<=1)
for(int j=0;j<(1<<len);j+=(i<<1))
for(int k=0;k<i;k++)
A[j+k]+=A[i+j+k]*opt;
}
signed main()
{
n=read();
for(int i=1;i<=n;i++){
scanf("%s",str+1);
for(int j=1;j<=n;j++)a[i][j]=str[j]-'0';
}for(int i=1;i<(1<<n);i++)cnt[i]=cnt[i>>1]+(i&1);
for(int i=1;i<(1<<n);i++)
for(int j=1;j<=n;j++)
if((1<<j-1)&i){
if(cnt[i]==1){f[j][i]=1;break;}
for(int k=1;k<=n;k++)
if((1<<k-1)&i)
f[j][i]+=f[k][i^(1<<j-1)]*a[j][k];
}
for(int i=1;i<(1<<n);i++)
for(int j=1;j<=n;j++)
g[i]+=f[j][i];
for(int i=1;i<=n;i++){
for(int j=0;j<(1<<n);j++)
if(cnt[j]==i)p[i][j]=g[j];FWTor(p[i],n,1);
}dfs(1,0,0);
for(int i=0;i<(1<<n-1);i++){
int top=0,tot=-1;
for(int j=0;j<n-1;j++)
if((i>>j&1)==0){
sta[++top]=j-tot;tot=j;
}sta[++top]=n-tot-1;sort(sta+1,sta+top+1);int P=1,ans=0;
for(int j=1;j<=top;j++)ans=(ans+1ll*sta[j]*P%mod)%mod,P=1ll*P*r%mod;F[i]=res[ans];
}n--;FWTand(F,n,-1);for(int i=0;i<(1<<n);i++)printf("%lld ",F[i]);
return 0;
}