[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 race 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
");
	 	 return 0;
	 }
	 K = _K;
	 init(N - K);
	 if (K == 0) {
	 	printf("%d
" , 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
" , ans);
     return 0;
}
原文地址:https://www.cnblogs.com/evenbao/p/14441309.html