题目链接

https://www.luogu.org/problemnew/show/P4931

题解

以下部分是我最开始的想法。

对于每一个 \(k\),满足恰好有 \(k\) 对情侣和睦的方案数为

\[\binom{n}{k} × \binom{n}{k} × k! × 2^k × f_{n - k}
\]

其中,\(f_x\) 表示 \(x\) 对情侣坐 \(x\) 排座位且没有任何一对情侣坐在同一排的方案数。

上述式子的意义为:从 \(n\) 对情侣中选出 \(k\) 对作为和睦的,再从 \(n\) 排中选出 \(k\) 排供这 \(k\) 对情侣就坐,方案数为 \(\binom{n}{k} × \binom{n}{k}\)。由于 \(k\) 对情侣就坐顺序可以不一致,且每队情侣内部双方也可以交换座位,因此方案数为 \(k! × 2^k\)。再乘以剩下的 \(n - k\) 对情侣均不出现和睦的方案数即为最终答案。

现在问题在于如何求出 \(f_x\)。考虑容斥,\(f_x=\) 任意就坐的方案数\(-\)至少存在一对情侣和睦的方案数\(+\)至少存在两对情侣和睦的方案数\(-\)至少存在三对情侣和睦的方案数\(...\)

那么做和上面类似的分析,我们可以得到:

\[f_x = \sum_{i = 0}^{x} (-1)^i × \binom{x}{i} × \binom{x}{i} × i!× 2^i × (2×(x - i))!
\]

上述式子最后的 \((2×(x - i))!\) 表示不强制和睦的 \(x - i\) 对情侣随意就坐的方案数。

总预处理的复杂度为 \(O(n^2)\)。

虽然这种容斥的想法很自然,但是时间复杂度并不优秀。我们需要找更好的办法求 \(f_x\)。于是就有了加强版。

在此之前,我们先回忆一下错位排列数的递推式:

\[D_n = (n - 1)(D_{n - 1} + D_{n - 2})(n \geq 2)
\]

《组合数学》上对其的证明大概是这样的:

我们想要求得 \(D_n\),考虑长度为 \(n\) 的排列的第一个位置,有 \(2, 3, 4, \cdots, n\) 共 \(n - 1\) 种填法。很显然的是,无论以谁作为开头,方案数都是一样的,因此我们只需算出以任意一个数开头的方案数,乘以 \(n - 1\) 就好了。我们假设第一位填了 \(2\),那么第二个位置可以填 \(1, 3, 4, \cdots, n\)。如果填了 \(1\),那么剩下的部分就是一个 \(3, 4, \cdots , n\) 的错位排列,方案数为 \(D_{n - 2}\);如果不填 \(1\),那么这一位的 \(1\) 和 \(3, 4, \cdots, n\) 共同构成了长度为 \(n - 1\) 的错位排列,方案数为 \(D_{n - 1}\),因此有 \(D_n = (n - 1)(D_{n - 1} + D_{n - 2})(n \geq 2)\)。

我们将其推广到这个题。若 \(n\) 对情侣要坐 \(n\) 排座位,考虑第一排座位必须坐两个不互为情侣的人,共有 \(2n \times (2n - 2) = 4n(n - 1)\) 种方案(第一个人可以是 \(2n\) 个人里面的任意一个,第二个人可以是剩下的 \(2n - 1\) 个人中除了第一个人的配偶的任意一个)。接下来共有两种情况:若第一排的两个人对应的配偶也坐到了同一排,那么剩下的 \(n - 2\) 对情侣就坐且不满足同排互为情侣的方案数为 \(f_{n - 2}\),由于第一排的两个人对应的配偶可以任选 \(n - 1\) 排中的一排就坐,且可以互换位置,因此该情况下总方案数为 \(2(n - 1)f_{n - 2}\);若第一排的两个人对应的配偶没有坐到同一排,那么我们可以把他们也视为一对情侣,他们和剩下的 \(n - 2\) 对情侣就坐合法的方案数为 \(f_{n - 1}\)。

这样,我们就得到了 \(f\) 的递推式:\(f_n = 4n(n - 1)(f_{n - 1} + 2(n - 1)f_{n - 2})(n \geq 2)\),边界为 \(f_0 = 1, f_1 = 0\)。此题就可以在 \(O(n)\) 的时间内完成预处理了。

代码

#include<bits/stdc++.h>

using namespace std;

#define X first
#define Y second
#define mp make_pair
#define pb push_back
#define debug(...) fprintf(stderr, __VA_ARGS__) typedef long long ll;
typedef long double ld;
typedef unsigned int uint;
typedef pair<int, int> pii;
typedef unsigned long long ull; template<typename T> inline void read(T& x) {
char c = getchar();
bool f = false;
for (x = 0; !isdigit(c); c = getchar()) {
if (c == '-') {
f = true;
}
}
for (; isdigit(c); c = getchar()) {
x = x * 10 + c - '0';
}
if (f) {
x = -x;
}
} template<typename T, typename... U> inline void read(T& x, U&... y) {
read(x), read(y...);
} template<typename T> inline bool checkMax(T& a, const T& b) {
return a < b ? a = b, true : false;
} template<typename T> inline bool checkMin(T& a, const T& b) {
return a > b ? a = b, true : false;
} const int N = 5e6 + 10, mod = 998244353; inline void mul(int& x, int y) {
x = 1ll * x * y % mod;
} inline int qpow(int v, int p) {
int res = 1;
for (; p; p >>= 1, mul(v, v)) {
if (p & 1) {
mul(res, v);
}
}
return res;
} int fac[N], invfac[N], pow2[N], f[N]; inline int binom(int n, int m) {
return 1ll * fac[n] * invfac[m] % mod * invfac[n - m] % mod;
} void init(int n) {
fac[0] = invfac[0] = pow2[0] = f[0] = 1;
for (register int i = 1; i <= n; ++i) {
fac[i] = 1ll * fac[i - 1] * i % mod;
pow2[i] = (pow2[i - 1] << 1) % mod;
if (i > 1) {
f[i] = 4ll * i * (i - 1) % mod * (f[i - 1] + 2ll * (i - 1) * f[i - 2] % mod) % mod;
}
}
invfac[n] = qpow(fac[n], mod - 2);
for (register int i = n - 1; i; --i) {
invfac[i] = 1ll * invfac[i + 1] * (i + 1) % mod;
}
} int main() {
init(5000000);
int t; read(t);
for (register int kase = 1; kase <= t; ++kase) {
int n, k; read(n, k);
printf("%lld\n", 1ll * binom(n, k) * binom(n, k) % mod * fac[k] % mod * pow2[k] % mod * f[n - k] % mod);
}
return 0;
}

最新文章

  1. 使用Cordova和JQM在ios上需要注意的问题
  2. ural 1222. Chernobyl’ Eagles
  3. SharePoint 2010 BCS - 简单实例(一)数据源添加
  4. (基础篇)PHP流程控制语句
  5. 545D. Queue
  6. Java中this,static,super及finalkeyword和代码块
  7. Python Counter()计数工具
  8. 64位下好神奇啊(增加了PatchGuard技术保护自己,SSDT是相对地址,参数通过寄存器与rdi来传递)
  9. [转载]mac下homebrew的使用
  10. java---Unicode-字符转换器
  11. Python学习笔记五--条件和循环
  12. 关于android socket出现at java.net.DatagramSocket java.net.BindException at libcore.io.IoBridge.bind(IoBridge.java:89)等waring
  13. 用MATLAB结合四种方法搜寻罗马尼亚度假问题
  14. 010 socket定义服务器
  15. RedirectToAction()转移方式及参数传递
  16. hdu 6006 Engineer Assignment 状压dp
  17. viewPager+fragment如何刷新缓存fragment
  18. SkylineGlobe 邻近度(Proximity)分析JavaScript源代码
  19. GCC编译过程与动态链接库和静态链接库
  20. 【hdu3709】 Balanced Number

热门文章

  1. Content 控件
  2. Linux3一些文件操作命令more,less,pr,head,tail,wc
  3. 十二.filter
  4. layer使用总结一配置
  5. Lambda03 方法引用、类型判断、变量引用
  6. 189. Rotate Array 从右边开始翻转数组
  7. 批量添加数据SqlBulkCopy
  8. PCL 平面模型分割
  9. 在Asp.Net中使用amChart统计图
  10. jqgrid常用操作