座位安排(容斥)

题面

有一张圆形的餐桌,这张餐桌有 2 n 2n 2n 个座位。现在有 n n n 对夫妻就坐,要求男士和女士隔位就坐,并且每对夫妻不能相邻,求安排座位的方案数。

答案可能很大,请对 998244353 \rm 998244353 998244353 取模。

3 ≤ n ≤ 1 e 5 3\leq n\leq 1e5 3n1e5.

样例输入

3

样例输出

2

样例解释

对于 3 3 3 对夫妻就坐的情况,安排座位的方案如下图所示:

在这里插入图片描述

题解

我们先强制把某个女士认为是序列头,然后拆环成链,只需要把最终答案数除以 n n n 就行。

我们想想,毫无限制的座位安排有多少种? ( n ! ) 2 (n!)^2 (n!)2 ,分别任意安排男士和女士。

然后,我们来容斥。我们把每对夫妻坐一起看作是一个条件,那么先打出“板子”:
∑ i = 0 n ( − 1 ) i . . . \sum_{i=0}^{n}(-1)^i... i=0n(1)i...

右边表示限定某 i \tt i i 条件满足的方案数,注意不是“ i i i 个条件,因为这样办不到。

对于每个 i i i ,我们首先得出这么 i i i 对夫妻,然后他们之间有相对顺序,用阶乘:
∑ i = 0 n ( − 1 ) i ⋅ i ! ⋅ C ( n , i ) . . . \sum_{i=0}^n(-1)^i\cdot i!\cdot C(n,i)... i=0n(1)ii!C(n,i)...

难点来了,把这 i i i 对夫妻安排在这个圆上,有两种情况:

  • # ♂♀ ### ♀♂ ##...## :没有夫妻跨越首尾,那么就相当于在序列上选择 i i i 段长度为 2 2 2 的不相交子串,我们把序列长度减去 i i i ,又等价于在序列中朴素地选 i i i 个位置了,因此方案数为 C ( 2 n − i , i ) C(2n-i,i) C(2ni,i)
  • ♀ #### ♂♀ ###...# ♂ :有一对夫妻在首尾相邻,我们把他们从首尾去除掉,那么就变成了长度减 2 2 2 、总夫妻数为 i − 1 i-1 i1 的上面那个问题了,方案数为 C ( 2 n − i − 1 , i − 1 ) C(2n-i-1,i-1) C(2ni1,i1)

因此,把这两者相加:
∑ i = 0 n ( − 1 ) i ⋅ i ! ⋅ C ( n , i ) ⋅ ( C ( 2 n − i , i ) + C ( 2 n − i − 1 , i − 1 ) ) . . . \sum_{i=0}^n(-1)^i\cdot i!\cdot C(n,i)\cdot\Big( C(2n-i,i)+C(2n-i-1,i-1) \Big)... i=0n(1)ii!C(n,i)(C(2ni,i)+C(2ni1,i1))...

剩下的没被选的男士女士们分别随便排,答案就是:
∑ i = 0 n ( − 1 ) i ⋅ i ! ⋅ C ( n , i ) ⋅ ( C ( 2 n − i , i ) + C ( 2 n − i − 1 , i − 1 ) ) ⋅ ( ( n − i ) ! ) 2 \sum_{i=0}^n(-1)^i\cdot i!\cdot C(n,i)\cdot\Big( C(2n-i,i)+C(2n-i-1,i-1) \Big)\cdot \big((n-i)!\big)^2 i=0n(1)ii!C(n,i)(C(2ni,i)+C(2ni1,i1))((ni)!)2

最后别忘除以 n n n

预处理阶乘法求组合数,再枚举,时间复杂度 O ( n ) O(n) O(n)

CODE

#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<ctime>
#include<queue>
#include<vector>
#include<bitset>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define MAXN 100005
#define LL long long
#define DB double
#define ENDL putchar('\n')
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
LL read() {
	LL f=1,x=0;char s = getchar();
	while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
	while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
	return f*x;
}
void putpos(LL x) {
	if(!x) return ;
	putpos(x/10); putchar('0'+(x%10));
}
void putnum(LL x) {
	if(!x) putchar('0');
	else if(x < 0) putchar('-'),putpos(-x);
	else putpos(x);
}
const int MOD = 998244353;
int n,m,s,o,k;
int fac[MAXN<<2],inv[MAXN<<2],invf[MAXN<<2];
int C(int n,int m) {
	if(m < 0 || m > n) return 0;
	return fac[n] *1ll* invf[n-m] % MOD *1ll* invf[m] % MOD;
}
int main() {
	n = read();
	fac[0]=fac[1]=inv[0]=inv[1]=invf[0]=invf[1]=1;
	for(int i = 2;i <= n*3;i ++) {
		fac[i] = fac[i-1]*1ll*i % MOD;
		inv[i] = (MOD-inv[MOD%i]) *1ll* (MOD/i) % MOD;
		invf[i] = invf[i-1] *1ll* inv[i] % MOD;
	}
	int ans = 0;
	for(int i = 0;i <= n;i ++) {
		int as = (C(2*n-i,i)+C(2*n-1-i,i-1)) % MOD *1ll* C(n,i) % MOD *1ll* fac[i] % MOD *1ll* fac[n-i] % MOD *1ll* fac[n-i] % MOD;
		(ans += ((i&1) ? (MOD-1):1) *1ll* as % MOD) %= MOD;
	}
	ans = ans *1ll* inv[n] % MOD;
	printf("%d\n",ans);
	return 0;
}
posted @ 2021-08-16 21:16  DD_XYX  阅读(71)  评论(0编辑  收藏  举报