Description

将 n 个车摆在 n × n 的棋盘上,每个格子最多摆放一个,并且每行每列和两条最长的对角线上至少有一个车,并且有 m 个格子不能摆放。问方案数。T组数据

[JZOJ6021]【GDOI2019模拟2019.2.15】车【容斥原理】【计数】_DP

Solution

每行每列至少有一个,且总个数等于行数,那就是每行每列恰好有一个。

我们发现m很小,显然这就是让你[JZOJ6021]【GDOI2019模拟2019.2.15】车【容斥原理】【计数】_bc_02容斥的
即强制选出一些不能选的位必须选,乘上(-1)^强制选的个数

这样相当于某些位置必须选,这些行列的其他格子不再可用。

考虑剩下的怎么做。

显然答案=没有任何限制方案数-左上右下对角线为空方案数-右上左下对角线为空方案数+两条对角线均为空的方案数。

一条对角线为空是容易计算的,同样采用容斥,我们先求出这条对角线上还有多少个格子可以用,枚举在这条对角线上强制选了多少个格子即可,剩下的行列直接阶乘随便选。

考虑两条对角线均为空。
也是容斥,假设两条对角线上分别选了x,y个格子,容斥系数可以推得就是(-1)^(x+y)
当n为奇数时,选取中心会让x,y都+1,所以此时的x+y要-1
也就是说容斥系数就是(-1)^两条对角线上总共选取的格子数,这样具体的x,y是无用的。

题解给出了一种非常巧妙的DP
我们从外向内一环一环的做,即对于一个i,考虑[JZOJ6021]【GDOI2019模拟2019.2.15】车【容斥原理】【计数】_DP_03这四个格子,将它们放到一起DP,显然环与环之间两两独立,每个环可以选0,1,2个格子。

那么就可以用背包做了,[JZOJ6021]【GDOI2019模拟2019.2.15】车【容斥原理】【计数】_背包_04表示做到第i个环,选了j个格子。
中心的一个相当于只有一个格子的特殊环。

最后统计答案同上面类似的。

总的时间复杂度[JZOJ6021]【GDOI2019模拟2019.2.15】车【容斥原理】【计数】_容斥原理_05

Code

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define N 115
#define mo 10007
using namespace std;
typedef long long LL;
int n,m,t,a[N],a1[12][2];
LL ans,js[N],ny[N],f[N][N];
bool bz[N],bf[N],bc[N];
LL ksm(LL k,LL n)
{
LL s=1;
for(;n;n>>=1,k=k*k%mo) if(n&1) s=s*k%mo;
return s;
}
LL C(int n,int m)
{
if(n<m) return 0;
return js[n]*ny[m]%mo*ny[n-m]%mo;
}
void dfs(int k,bool p1,bool p2,LL v,int c)
{
if(k>m)
{
LL s1=js[c],s2=0,s3=0,s4=0;
if(!p1)
{
int cnt=0;LL vp=1;
fo(i,0,n-1) if(!bf[i]&&!bc[i]) cnt++;
fo(i,0,cnt)
{
s2=(s2+vp*js[c-i]*C(cnt,i)%mo+mo)%mo;
vp=-vp;
}
}
if(!p2)
{
int cnt=0;LL vp=1;
fo(i,0,n-1) if(!bf[i]&&!bc[n-1-i]) cnt++;
fo(i,0,cnt)
{
s3=(s3+vp*js[c-i]*C(cnt,i)%mo+mo)%mo;
vp=-vp;
}
}
if(!p1&&!p2)
{
memset(f,0,sizeof(f));
f[0][0]=1;
fo(i,0,(n+1)/2-1)
{
fo(j,0,2*(i+1))
{
f[i+1][j]=f[i][j];
if(n-1-i!=i)
{
if(j>0)
{
LL c1=(!bf[i]&&!bc[i])+(!bf[i]&&!bc[n-1-i])+(!bf[n-1-i]&&!bc[i])+(!bf[n-1-i]&&!bc[n-1-i]);
f[i+1][j]=(f[i+1][j]+f[i][j-1]*c1)%mo;
}
if(j>1)
{
LL c1=2*(!bf[i]&&!bc[i]&&!bf[n-1-i]&&!bc[n-1-i]);
f[i+1][j]=(f[i+1][j]+f[i][j-2]*c1)%mo;
}
}
else if(!bf[i]&&!bc[i]&&j>0) f[i+1][j]=(f[i+1][j]+f[i][j-1])%mo;
}
}
LL vp=1;
fo(i,0,n) s4=(s4+vp*js[c-i]*f[(n+1)/2][i]%mo+mo)%mo,vp=-vp;
}
ans=(ans+v*(s1-s2-s3+s4)%mo+mo)%mo;
return;
}
dfs(k+1,p1,p2,v,c);
int x=a1[k][0],y=a1[k][1];
if(!bf[x]&&!bc[y])
{
bf[x]=1,bc[y]=1;
dfs(k+1,p1||(x==y),p2||(x==n-1-y),-v,c-1);
bf[x]=0,bc[y]=0;
}
}
int main()
{
cin>>t;
js[0]=1;
fo(i,1,100) js[i]=js[i-1]*(LL)i%mo;
ny[100]=ksm(js[100],mo-2);
fod(i,99,0) ny[i]=ny[i+1]*(LL)(i+1)%mo;
while(t--)
{
scanf("%d%d",&n,&m);
memset(bf,0,sizeof(bf));
memset(bc,0,sizeof(bc));
ans=0;
fo(i,1,m) scanf("%d%d",&a1[i][0],&a1[i][1]);
dfs(1,0,0,1,n);
printf("%lld\n",ans);
}
}