CF1342E Placing Rooks 题解
首先答案满足每行有一个棋子或每列有一个棋子,否则假设某一行没有棋子,则为了覆盖这一行需要每一列都有一个棋子,矛盾。某一列没有棋子同理。因此,最多只能把棋子摆成一排,产生 \(k-1\) 对碰撞。
行和列等价,我们先考虑每行有一个棋子的情况。假设每一列都只有一颗棋子,如果需要产生 \(k\) 对棋子可以相互攻击,则需要将 \(k\) 列的棋子移动到别的列,最后总共有 \(n-k\) 列有棋子。
首先选出 \(n-k\) 列的编号,方案数为 \(\binom{n}{n-k}\)。然后假设每列相同,由于棋子所在的行不同所以本质不同,相当于有区别的求放进无区别的盒子里,方案数为 \(n\brace n-k\)。最后由于每列不同,分配编号,方案数为 \((n-k)!\)。最终的式子为 \(\binom{n}{n-k}{n\brace n-k}(n-k)!\)。第二类斯特林数可以直接使用通项公式计算。
注意 \(k\ne0\) 时还需要考虑每列有一个棋子的情况,与每行有一个棋子的情况相同,乘以 \(2\) 即可。
#include <bits/stdc++.h>
using namespace std;
long long n,k,jc[300000],inv[300000],ans=0;
const long long mod=998244353;
long long power(long long a,long long p)
{
long long x=a,ans=1;
while(p)
{
if(p&1)ans=ans*x%mod;
p>>=1;
x=x*x%mod;
}
return ans;
}
long long c(long long n,long long k)
{
return jc[n]*inv[n-k]%mod*inv[k]%mod;
}
long long strl(long long n,long long m)
{
long long ans=0;
for(int i=0;i<=m;i++)ans=(ans+power(-1,m-i)*power(i,n)%mod*inv[i]%mod*inv[m-i]%mod)%mod;
return (ans%mod+mod)%mod;
}
int main()
{
jc[0]=1;
for(int i=1;i<=200000;i++)jc[i]=jc[i-1]*i%mod;
inv[200000]=power(jc[200000],mod-2)%mod;
for(int i=199999;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod;
scanf("%lld%lld",&n,&k);
if(k>=n)printf("0\n");
else if(k==0)printf("%lld\n",c(n,n-k)%mod*strl(n,n-k)%mod*jc[n-k]%mod);
else printf("%lld\n",2*c(n,n-k)%mod*strl(n,n-k)%mod*jc[n-k]%mod);
return 0;
}

浙公网安备 33010602011771号