JZOJ6724. 【2020.06.15省选模拟】T1

Description

在这里插入图片描述
n < = 17 , Q < = 1 e 5 n<=17,Q<=1e5 n<=17,Q<=1e5

Solution

  • 考虑容斥,设一个长度为 n − 1 n-1 n1的0/1串,0的位置 a i = 0 / 1 a_i=0/1 ai=0/1,1的位置 a i = 1 a_i=1 ai=1,这个状态对应的 a i a_i ai的排列的方案数。
  • 那么对于一个 00100110110... 00100110110... 00100110110...,相邻的 1 1 1表示这条链一定里面全是1, 0 0 0则没有限制,那么这个串就把 n n n个点分成了若干个互补相关的链。
  • 考虑这种分法一共有 n n n的整数划分 P ( n ) = 297 P(n)=297 P(n)=297种,可以暴力所有的分法,那么要求所有的链的并集为 n n n
  • 如果我们把每一种链可行的集合找出来,这个集合要选出 c [ i ] c[i] c[i]条链(不可重),最后拼出 n n n。这实际上相当于是一个子集卷积。
  • 因为总数就是n,所以贡献到 2 n − 1 2^n-1 2n1位置一定不会有重复的点,所以直接FWT即可。预先处理点值,递归的时候乘一乘,用简单的DP做链的状况。
  • 求出每一种01串的方案数,就可以反演回去了。因为原先的形式相当于是And卷积的形式,所以IFWT回去就好(也可以理解为高维前缀和的逆运算)。
  • 一道灵活运用FWT子集转移的题目
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define ll long long
#define maxn 17
using namespace std;

int n,q,i,j,k,a[maxn][maxn],cnt[1<<maxn],d[maxn+1][1<<maxn],c[maxn+1];
ll f[1<<maxn][maxn],g[maxn+1][1<<maxn];

void fwt(ll *a){
    
    
	int N=1<<n;
	for(int i=1;i<N;i<<=1)
		for(int j=0;j<N;j+=i<<1)
			for(int k=0;k<i;k++)
				a[j+k+i]+=a[j+k];
}

void ifwt(ll *a){
    
    
	int N=1<<n;
	for(int i=1;i<N;i<<=1)
		for(int j=0;j<N;j+=i<<1)
			for(int k=0;k<i;k++)
				a[j+k]-=a[j+k+i]; 
}

ll F[maxn][1<<maxn],G[1<<maxn];
void cover(int s,ll sum,int S){
    
    
	if (s==n){
    
    
		G[S]+=sum;return;
	}
	for(int i=1;i<=n;i++) if (c[i])
		c[i]--,cover(s+i,sum,(S<<i)|((1<<i-1)-1)),c[i]++;
}

void dg(int i,int res){
    
    
	if (res&&(res<i||i>n)) return;
	if (i>n||res==0){
    
    
		for(int j=i;j<=n;j++) c[j]=0;
		ll sum=0;
		for(int j=0;j<1<<n;j++)
			sum+=F[i-1][j]*(((n-cnt[j])&1)?-1:1);
		cover(0,sum,0);
		return;
	}
	for(int j=0;i*j<=res;j++){
    
    
		c[i]=j,memcpy(F[i],F[i-1],sizeof(F[i]));
		dg(i+1,res-i*j);
		for(int k=0;k<1<<n;k++) F[i-1][k]=F[i-1][k]*g[i][k];
	}
}

int main(){
    
    
//	freopen("s1mple.in","r",stdin);
//	freopen("s1mple.out","w",stdout);
	scanf("%d",&n); char ch=getchar();
	for(i=0;i<n;i++){
    
    
		while (ch!='0'&&ch!='1') ch=getchar();
		for(j=0;j<n;j++) a[i][j]=ch-'0',ch=getchar();
	}
	for(i=0;i<n;i++) f[1<<i][i]=1;
	for(int S=0;S<1<<n;S++) for(i=0;i<n;i++) if (f[S][i])
		for(j=0;j<n;j++) if (!(S>>j&1)&&a[i][j])
			f[S|(1<<j)][j]+=f[S][i];
	for(i=1;i<1<<n;i++) {
    
    
		cnt[i]=cnt[i>>1]+(i&1);
		d[cnt[i]][++d[cnt[i]][0]]=i;
	}
	for(i=1;i<=n;i++) {
    
    
		for(j=1;j<=d[i][0];j++) {
    
    
			int S=d[i][j];
			for(k=0;k<n;k++) if (S>>k&1)
				g[i][S]+=f[S][k];
		}
		fwt(g[i]);
	}
	for(i=0;i<1<<n;i++) F[0][i]=1;
	dg(1,n);
	ifwt(G);
	scanf("%d",&q),ch=getchar();
	while (q--){
    
    
		while (ch!='0'&&ch!='1') ch=getchar();
		int P=0; for(i=1;i<n;i++) P=P<<1|(ch-'0'),ch=getchar();
		printf("%lld\n",G[P]);
	}
}

猜你喜欢

转载自blog.csdn.net/qq_43649416/article/details/106771651