Count

题目大意

给一个\(n\times n(n\leq 32)\)的网格,你需要选定\(C\)个格子,要求每行每列至少有一个格子,主对角线和副对角线至少有一个格子,有\(k(k\leq 7)\)个格子不能选,问方案数。

题解

容斥这个不用多说吧。。。

首先对障碍点做容斥,其次对角线这个限制比较麻烦,不好和横竖同时处理。

那么我们先对两条对角线做容斥,然后再去考虑行和列的限制。

\(f(i,j,k)\)表示有\(i\)行和\(j\)列是可以使用的,因为之前容斥的对角线的原因有\(k\)个格子不能选的方案数。

这个可以用背包来解决,在进行背包转移的时候,枚举对称的两行两列,再\(2^{16}\)枚举这两行两列的选择情况,根据对角线的容斥情况计算第三维的更新值。

代码

代码可以认为是抄的逆十字

#include<bits/stdc++.h>
#define N 1309
#define M 33
using namespace std;
typedef long long ll;
const int mod=10007;
const int maxn=1200;
int c[N][N],b[N];
int x[N],y[N];
int dp[M][M][M<<1],g[M][M][M<<1];
int tagx[M],tagy[M];
int n,k,m;
inline ll rd(){
	ll x=0;char c=getchar();bool f=0;
	while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
	while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f?-x:x;
}
inline void MOD(int &x){x=x>=mod?x-mod:x;}
inline void pre_work(){
	for(int i=1;i<=maxn;++i)b[i]=b[i>>1]+(i&1);
	c[0][0]=1;
	for(int i=1;i<=maxn;++i){
		c[i][0]=1;
		for(int j=1;j<=i;++j)
		    MOD(c[i][j]=c[i-1][j]+c[i-1][j-1]); 
	}
}
int calc(int zh,int fu,int cnt){
    memset(dp,0,sizeof(dp));
    dp[0][0][0]=1;
    int siz=0; 
    for(int l=1,r=n;l<=r;l++,r--){
    	for(int i=0;i<=siz;++i)
    		for(int j=0;j<=siz;++j)
    		    for(int k=0;k<=siz*2;++k)if(dp[i][j][k]){
    		    	if(l!=r){
    		        	for(int i1=tagx[l];i1<=1;++i1)
    		        	    for(int i2=tagy[l];i2<=1;++i2)
    		        	         for(int i3=tagx[r];i3<=1;++i3)
								     for(int i4=tagy[r];i4<=1;++i4){
								     	int ni=i1+i3+i,nj=j+i2+i4,nk=k;
								     	if(zh&&i1&&i2)nk++;
								     	if(zh&&i3&&i4)nk++;
										if(fu&&i1&&i4)nk++;
										if(fu&&i2&&i3)nk++;
										if(i1^i2^i3^i4)
										     MOD(g[ni][nj][nk]+=mod-dp[i][j][k]);
										else MOD(g[ni][nj][nk]+=dp[i][j][k]); 
								 }
					}
					else{
						for(int i1=tagx[l];i1<=1;++i1)
    		        	    for(int i2=tagy[l];i2<=1;++i2){
    		        	    	int ni=i+i1,nj=j+i2,nk=k;
    		        	    	if(i1&&i2&&(zh||fu))nk++;
    		        	    	if(i1^i2)MOD(g[ni][nj][nk]+=mod-dp[i][j][k]);
    		        	    	else MOD(g[ni][nj][nk]+=dp[i][j][k]);
    		        	    }
					}       
	            }
		if(l==r)siz++;else siz+=2; 
	    for(int i=0;i<=siz;++i)
	        for(int j=0;j<=siz;++j)
	            for(int k=0;k<=siz*2;++k){
	            	dp[i][j][k]=g[i][j][k];
	            	g[i][j][k]=0;
	            }
	}
	int ans=0;
	for(int i=0;i<=siz;++i)
	    for(int j=0;j<=siz;++j)
		    for(int k=0;k<=siz*2;++k){
		    	if(i*j-k-cnt>=0){
				     MOD(ans+=dp[i][j][k]*c[i*j-cnt-k][m]%mod);
				}
		    }
    return ans;
}
int solve(int s){
	m-=b[s];
	if(m<0){
	   m+=b[s];
	   return 0;
    }
	int zh=0,fu=0;
	for(int j=0;j<k;++j)if((1<<j)&s){
		tagx[x[j]]=1;
		tagy[y[j]]=1;
		if(x[j]==y[j])zh=1;
		if(x[j]+y[j]==n+1)fu=1;
	}
	int ans=calc(0,0,b[s]);
	if(!zh)MOD(ans+=mod-calc(1,0,b[s]));
	if(!fu)MOD(ans+=mod-calc(0,1,b[s]));
	if(!zh&&!fu)MOD(ans+=calc(1,1,b[s]));
	m+=b[s];
	for(int j=0;j<k;++j)if((1<<j)&s){
		tagx[x[j]]=0;
		tagy[y[j]]=0;
	}
	return ans;
}
int main(){
	pre_work(); 
	n=rd();k=rd();m=rd();
	for(int i=0;i<k;++i){
		x[i]=rd();
		y[i]=rd();
	}
	int ans=0;
	for(int i=0;i<(1<<k);++i){
		if(b[i]&1)MOD(ans+=mod-solve(i));
		else MOD(ans+=solve(i));
	}
	cout<<ans<<endl;
	return 0;
}

posted @ 2021-08-01 21:28  comld  阅读(239)  评论(0编辑  收藏  举报