容斥无止境。
看到 m m m很小,方案很难算那肯定是容斥了。
先考虑 m = 0 m=0 m=0的情况:
我们考虑用总方案减去单独至少一条对角线上没有,再加上两条对角线上同时没有的方案。
因为是 n n n个车, n n n行 n n n列,那么每一列要放一个,且不能同行。
那么总方案显然是: n ! n! n!。
考虑单独一条对角线:
我们考虑枚举放多少个在该条对角线上,容斥系数是 ( − 1 ) i (-1)^i (−1)i。
方案就是 ( i n ) ∗ ( n − i ) ! (^n_i)*(n-i)! (in)∗(n−i)!。另一条对角线同理。
两条对角线同时没有:
我们枚举放多少个在第一条对角线上,放多少个在第二条对角线上。
那么容斥系数是 ( − 1 ) i + j (-1)^{i+j} (−1)i+j,相当于在枚举放的位置的子集。
方案如何算?因为格子会互相影响,所以并不是简单地组合数。
我们发现互相影响的一定是 ( i , i ) , ( i , n − i + 1 ) , ( n − i + 1 , n − i + 1 ) , ( n − i + 1 , i ) (i,i),(i,n-i+1),(n-i+1,n-i+1),(n-i+1,i) (i,i),(i,n−i+1),(n−i+1,n−i+1),(n−i+1,i)这四个格子。
那么我们可以从最外层的矩形一层一层往内层 D P DP DP,设 f i f_i fi表示一共放 i i i个的方案。
然后每一层枚举选几个即可。
再考虑 m ! = 0 m!=0 m!=0的情况:
我们不妨再次容斥,考虑强制选其中一些位置,然后用上面的方法去解决。
然后对于情况分别讨论即可。
复杂度: O ( T ∗ n 2 ∗ 2 m ) O(T*n^2*2^m) O(T∗n2∗2m)。
# include
# include
# include
using namespace std;
const int N = 1e2 + 5;
const int mo = 1e4 + 7;
typedef long long ll;
int p[N][2],vis[N][2],C[N][N];
int f[N],g[N],fac[N];
int n,m,T,R;
inline int Z()
{
int L = n,ret = 0;
for (int i = 1 ; i <= n ; ++i) if (vis[i][0] || vis[i][1]) --L;
for (int i = 0 ; i <= R ; ++i) ret = (ret + ((i & 1) ? -1 : 1) * (ll)C[L][i] * fac[R - i] % mo + mo) % mo;
return ret;
}
inline int Y()
{
int L = n,ret = 0;
for (int i = 1 ; i <= n ; ++i) if (vis[i][0] || vis[n - i + 1][1]) --L;
for (int i = 0 ; i <= R ; ++i) ret = (ret + ((i & 1) ? -1 : 1) * (ll)C[L][i] * fac[R - i] % mo + mo) % mo;
return ret;
}
inline int A()
{
int y = 0,e = 0,s = 0,ret = 0;
for (int l = 1,r = n ; l <= r ; ++l,--r)
{
int fir = 2,sec = 2;
if (l == r) --fir,--sec;
if (vis[l][0]) --fir; if (vis[l][1]) --sec;
if (!fir || !sec) continue;
if (vis[r][0]) --fir; if (vis[r][1]) --sec;
if (fir * sec == 1) ++y;
else if (fir * sec == 2) ++e;
else if (fir * sec == 4) ++s;
} memset(f,0,sizeof(f)),f[0] = 1;
for (int i = 1 ; i <= y ; ++i)
{
memset(g,0,sizeof(g));
for (int j = 0 ; j <= R ; ++j) g[j] = (f[j] + (j ? f[j - 1] : 0)) % mo;
memcpy(f,g,sizeof(f));
}
for (int i = 1 ; i <= e ; ++i)
{
memset(g,0,sizeof(g));
for (int j = 0 ; j <= R ; ++j) g[j] = (f[j] + (j ? 2ll * f[j - 1] : 0)) % mo;
memcpy(f,g,sizeof(f));
}
for (int i = 1 ; i <= s ; ++i)
{
memset(g,0,sizeof(g));
for (int j = 0 ; j <= R ; ++j) g[j] = (f[j] + (j ? 4ll * f[j - 1] : 0) + (j > 1 ? 2ll * f[j - 2] : 0)) % mo;
memcpy(f,g,sizeof(f));
}
for (int i = 0 ; i <= R ; ++i) ret = (ret + ((i & 1) ? -1 : 1) * (ll)f[i] * fac[R - i] % mo + mo) % mo;
return ret;
}
int main()
{
// freopen("a.in","r",stdin);
freopen("rook.in","r",stdin);
freopen("rook.out","w",stdout);
scanf("%d",&T),fac[0] = C[0][0] = 1;
while (T--)
{
scanf("%d%d",&n,&m); int lim = 1 << m,ans = 0;
for (int i = 1 ; i <= m ; ++i) scanf("%d%d",&p[i][0],&p[i][1]),++p[i][0],++p[i][1];
for (int i = 1 ; i <= n ; ++i)
{
fac[i] = (ll)fac[i - 1] * i % mo,C[i][0] = C[i][i] = 1;
for (int j = 1 ; j < i ; ++j) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % mo;
}
for (int i = 0 ; i < lim ; ++i)
{
bool pd = 0,fir = 0,sec = 0; memset(vis,0,sizeof(vis)),R = n;
for (int j = 1 ; j <= m ; ++j)
if (i & (1 << j - 1))
{
if (vis[p[j][0]][0]) { pd = 1; break; }
if (vis[p[j][1]][1]) { pd = 1; break; }
if (p[j][0] == p[j][1]) fir = 1;
if (p[j][0] == n - p[j][1] + 1) sec = 1;
vis[p[j][0]][0] = vis[p[j][1]][1] = 1,--R;
} if (pd) continue;
int X = (n - R) & 1 ? -1 : 1;
if (fir && sec) ans = (ans + X * fac[R] + mo) % mo;
else if (fir && !sec) ans = (ans + X * (fac[R] - Y()) + mo) % mo;
else if (!fir && sec) ans = (ans + X * (fac[R] - Z()) + mo) % mo;
else ans = (ans + X * ((ll)fac[R] - Z() - Y() + A()) + 2ll * mo) % mo;
} printf("%d\n",ans);
}
return 0;
}