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;
}