[CF1342E] Placing Rooks
前言
数学就要多练练
题目
讲解
首先易证当\(n \le k\)时无解,\(k=0\)时答案为\((n-1)!\)
此时由于行和列是等价的,所以我们先只考虑对于每行只放一个车,最后将答案乘二即可
考虑放下一个车之后,如果当前列没有车,则对攻击数量不会有贡献,我们希望有\(k\)对车可以和互相攻击,则我们要放\(n-k\)列
对于\(n\)行,每行有\(n-k\)个位置可以放,则答案为\(C_n^{n-k}*(n-k)^n\)
吗?
很明显这样是错的,这样的统计会将放\(\le n-k\)列的方案全部算进去,但我们需要的是恰好放\(n-k\)列的方案
典型的容斥,设\(n-k\)列中有\(i\)列一定为空,容斥式子为:
\[S=\sum_{i=0}^{n-k}(-1)^i*C_{n-k}^{i}*(n-k-i)^n
\]
最终答案即为:
\[C_n^{n-k}*S*2
\]
代码
int qpow(int x,int y)
{
int ret = 1;
while(y){if(y & 1) ret = 1ll * ret * x % MOD;x = 1ll * x * x % MOD;y >>= 1;}
return ret;
}
int fac[MAXN],ifac[MAXN];
void pre(int x)
{
fac[0] = 1;
for(int i = 1;i <= x;++ i) fac[i] = 1ll * fac[i-1] * i % MOD;
ifac[x] = qpow(fac[x],MOD-2);
for(int i = x-1;i >= 0;-- i) ifac[i] = 1ll * ifac[i+1] * (i+1) % MOD;
}
int C(int x,int y)
{
if(x < y) return 0;
return 1ll * fac[x] * ifac[y] % MOD * ifac[x-y] % MOD;
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = Read(); k = Read();
if(k >= n){Put(0);return 0;}
pre(200000);
if(!k){Put(fac[n]);return 0;}
for(int i = 0;i <= n-k;++ i)
{
LL f = (i&1) ? -1 : 1;
ans = (ans + f * C(n-k,i) * qpow(n-k-i,n)) % MOD;
}
ans = ans * C(n,n-k) * 2 % MOD;
Put((ans + MOD) % MOD);
return 0;
}