[Luogu P4921] [Luogu P4931] 情侣 给我烧了(加强版)

洛谷传送门1

洛谷传送门2

题目描述

n n n 对情侣来到电影院观看电影。在电影院,恰好留有 n n n 排座位,每排包含 2 2 2 个座位,共 2 × n 2×n 2×n 个座位。

现在,每个人将会随机坐在某一个位置上,且恰好将这 2 × n 2 × n 2×n 个座位坐满。

如果一对情侣坐在了同一排的座位上,那么我们称这对情侣是和睦的。

你的任务是求出共有多少种不同的就坐方案满足恰好 k k k 对情侣是和睦的。

两种就坐方案不同当且仅当存在一个人在两种方案中坐在了不同的位置。不难发现,在没有任何限制条件的情况下,每个人任意就坐一共会有 ( 2 n ) ! (2n)! (2n)! 种不同的就坐方案。

输入输出格式

输入格式:

输入包含多组数据。

输入的第一行包含一个正整数 T T T,表示数据组数。

接下来 T T T 行,每行包含两个非负整数 n , k n, k n,k,其意义见题目描述。

输出格式:

对于每组输入数据,输出一行,表示对应的就坐方案数。由于结果可能较大,因此输出对 998244353 998244353 998244353 取模的结果。

输入输出样例

输入样例#1:

5
1 1
2 0
2 2
2333 666
2333333 1000000

输出样例#1:

2
16
8
798775522
300377435

说明

对于 10 % 10 \% 10% 的数据,满足 1 ≤ T ≤ 10 , 1 ≤ n ≤ 5 1 \leq T \leq 10, 1 \leq n \leq 5 1T10,1n5
对于 40 % 40 \% 40% 的数据,满足 1 ≤ n ≤ 3 × 1 0 3 1 \leq n \leq 3 \times 10^3 1n3×103
对于 100 % 100 \% 100% 的数据,满足 1 ≤ T ≤ 2 × 1 0 5 , 1 ≤ n ≤ 5 × 1 0 6 , 0 ≤ k ≤ 1 \leq T \leq 2 \times 10^5, 1 \leq n \leq 5 \times 10^6, 0 \leq k \leq 1T2×105,1n5×106,0k n

解题分析

这道题可以利用 O ( N 2 ) O(N^2) O(N2)处理错排得到科学的答案, 然而这里需要 O ( N ) O(N) O(N)递推。

首先我们可以得到这样的一个式子:
a n s = ( n k ) 2 × k ! × 2 k × D [ n − k ] ans=\binom{n}{k}^2\times k!\times 2^k\times D[n-k] ans=(kn)2×k!×2k×D[nk]
具体而言是这样的: n n n对里面选出 k k k对, 可以放在 ( n k ) \binom{n}{k} (kn)个位置上, 然后还有 k ! k! k!种排列方式。 这些情侣可以交换左右, 所以要乘上 2 k 2^k 2k。 后面的 D [ n − k ] D[n-k] D[nk]表示剩下的人的错排方案数。

那么问题就在这个错排数了。 考虑我们已经推到了 D [ n − 1 ] D[n-1] D[n1], 考虑第 n n n对。

那么我们可以选一个位置先让一个人做下, 显然有 2 n 2n 2n个。 然后从其他对里面抽一个人过来和他配对, 共有 2 n − 2 2n-2 2n2种方案。

现在我们再考虑这两对中的没有配对的另外两个人。

  • 如果这两对互相配对了, 那么其他两个人可以选 n − 1 n-1 n1排中的任何一排,并且可以左右互换, 然后问题就变成了 n − 2 n-2 n2对的错排数, 所以方案数为 2 × ( n − 1 ) × D [ n − 2 ] 2\times (n-1)\times D[n-2] 2×(n1)×D[n2]
  • 如果没有配对, 那么我们可以假设他们是一对, 然后问题就变成了 n − 1 n-1 n1对的错排数。

所以递推式为 D [ n ] = 2 n ( 2 n − 2 ) ( D [ n − 1 ] + 2 ( n − 1 ) D [ n − 2 ] ) D[n]=2n(2n-2)(D[n-1]+2(n-1)D[n-2]) D[n]=2n(2n2)(D[n1]+2(n1)D[n2])

然后处理出阶乘、阶乘逆元、 2 2 2的次幂和错排数即可 O ( 1 ) O(1) O(1)回答。

代码如下:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 5000050
#define MOD 998244353ll
#define ll long long
int fac[MX], inv[MX], pw[MX], D[MX];
void pre()
{
	fac[0] = inv[0] = inv[1] = fac[1] = pw[0] = D[0] = 1; pw[1] = 2;
	for (R int i = 2; i <= 5e6; ++i) 
	{
		fac[i] = 1ll * fac[i - 1] * i % MOD;
		inv[i] = 1ll * inv[MOD % i] * (MOD - MOD / i) % MOD;
		pw[i] = pw[i - 1] * 2 % MOD;
		D[i] = 1ll * 4 * i * (i - 1) % MOD * ((D[i - 1] + 1ll * D[i - 2] * 2 % MOD * (i - 1) % MOD) % MOD) % MOD;
	}
	for (R int i = 2; i <= 5e6; ++i) inv[i] = 1ll * inv[i - 1] * inv[i] % MOD;
}
IN int C(R int n, R int m) {return 1ll * fac[n] * inv[m] % MOD * inv[n - m] % MOD;}
IN int calc(R int n, R int k) {return 1ll * C(n, k) * C(n, k) % MOD * fac[k] % MOD * pw[k] % MOD * D[n - k] % MOD;}
int main(void)
{
	using namespace std;
	ios_base::sync_with_stdio(false);
	int T, n, m;
	cin >> T; pre();
	W (T--)
	{
		cin >> n >> m;
		cout << calc(n, m) << endl;
	}
}

你可能感兴趣的:(数学,排列组合)