[CF1342E] Placing Rooks
[题目链接]
http://codeforces.com/contest/1342/problem/E
[题解]
首先当 \(K \geq N\) 时答案显然为 \(0\)。
一个观察是 : 要么每行都有车 , 要么每列都有。
这两者是独立的 , 只需考虑一种最后将答案乘 \(2\) 即可。
也就是要将 \(N\) 个棋子放入 \((N - K)\) 个不同的列中。
方案数为 \({N \choose K} \cdot (N - K)! \cdot {N \brace K}\)
可以通过容斥求斯特林数。
时间复杂度 : \(O(N)\)
[代码]
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i , l , r) for (int i = (l); i < (r); ++i)
const int MN = 2e5 + 5 , mod = 998244353;
int fac[MN] , ifac[MN] , pw[MN] , pr[MN] , N , K;
inline void inc(int &x , int y) {
x = x + y < mod ? x + y : x + y - mod;
}
inline void dec(int &x , int y) {
x = x - y >= 0 ? x - y : x - y + mod;
}
inline int qPow(int a , int b) {
int c = 1;
for (; b; b >>= 1 , a = 1ll * a * a % mod) if (b & 1) c = 1ll * c * a % mod;
return c;
}
inline void init(int n) {
fac[0] = 1;
for (int i = 1; i <= n; ++i) fac[i] = 1ll * fac[i - 1] * i % mod;
ifac[n] = qPow(fac[n] , mod - 2);
for (int i = n - 1; i >= 0; --i) ifac[i] = 1ll * ifac[i + 1] * (i + 1) % mod;
return;
}
inline void sieve(int n , int k) {
int cnt = 0;
pw[1] = 1;
for (int i = 2; i <= n; ++i) {
if (!pw[i]) {
pr[++cnt] = i;
pw[i] = qPow(i , k);
}
for (int j = 1; j <= cnt && i * pr[j] <= n; ++j) {
pw[i * pr[j]] = 1ll * pw[i] * pw[pr[j]] % mod;
if (i % pr[j] == 0) break;
}
}
}
inline int S(int n , int m) {
sieve(m , n);
int res = 0;
for (int i = 1; i <= m; ++i) {
int val = 1ll * pw[i] * ifac[i] % mod * ifac[m - i] % mod;
if ((m - i) & 1) dec(res , val); else inc(res , val);
}
return res;
}
int main() {
LL _K;
scanf("%d%lld" , &N , &_K);
if (_K >= N) {
printf("0\n");
return 0;
}
K = _K;
init(N - K);
if (K == 0) {
printf("%d\n" , fac[N]);
return 0;
}
int ans = 2LL * S(N , N - K) % mod;
for (int i = N; i > K; --i) ans = 1LL * ans * i % mod;
printf("%d\n" , ans);
return 0;
}