Two Sided Cards 题解

前言

五一网课的例题,但是网上没有详细的题解(真的连题解都找不到啊),所以来写一篇,就当攒 RP 了。题目可以在这里提交。原题是 TopCoder - 10947,但是有了账号也交不了?

题目简述

\(n\) 张卡片,正面和反面分别组成了 \(1 \sim n\) 的排列。现在你需要将这 \(n\) 张卡片排成一排。卡片可以以任何顺序放置,每张卡片可以显示正面或背面,排成的序列不一定是一个排列。两种方案至少存在一个位置的数字不同,则这两种方案被认为是不同的。求排成的不同的序列的方案数,答案对 \(10^9+7\) 取模。

题目分析

套路地,把卡牌当做边,正面连向反面,建图。发现形成了若干个环。环之间互不影响,所以我们只要求得一个环的方案数 \(W_i\) 即可。进而发现设环的长度为 \(L_i\),则 \(W_i\) 只跟 \(L_i\) 有关,即 \(W_i = f(L_i)\)

考虑计算 \(f(L)\)。发现方案数和重合的数字个数有关,而每个数字要么出现一次,要么出现两次。故枚举有 \(i\) 个数字出现了两次,求出方案数,总方案数即为所有情况之积。从 \(L\) 个位置中选出 \(2i\) 个位置是重复的,方案数是 \(C_L^{2i}\),若 \(i \neq 0\),则还要乘 \(2\),因为将方案取反可以得到新的方案。接下来,不断从剩下可选的位置中选出重复的那 \(2\) 个位置,这 \(2\) 个位置没有区分。剩下的数随随便排列,方案数是 \((L-2i)!\)。这里方案数是 \(\prod \limits _ {j = 1} ^ {i} C _ {L - 2(j-1)} ^ 2\),化简得 \(\cfrac{n!}{2^i \times (L-2i)!}\)。综上:

\[ f(L) = \sum \limits _ {i = 0} ^ {\left \lfloor \cfrac{L}{2} \right \rfloor} C_L^{2i} \times (2 - [i = 0]) \times \cfrac{L!}{2 ^ i} \]

故总答案:

\[ ans = \Large \prod \limits _ {i = 1} ^ {cnt} C_{n - \sum \limits _ {j = 1} ^ {i - 1} L_j}^{L_i} \times f(L_i) \]

总体时间复杂度是 \(\Theta(n)\) 的。

代码

//#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx", "sse2", "sse3", "sse4", "mmx")
#include <iostream>
#include <cstdio>
#define debug(a) cerr << "Line: " << __LINE__ << " " << #a << endl
#define print(a) cerr << #a << "=" << (a) << endl
#define file(a) freopen(#a".in", "r", stdin), freopen(#a".out", "w", stdout)
#define main Main(); signed main(){ return ios::sync_with_stdio(0), cin.tie(0), Main(); } signed Main
using namespace std;

const int mod = 1000000007;
const int inv2 = 500000004;

inline int add(int a, int b){
	return a + b >= mod ? a + b - mod : a + b;
}

inline int mul(int a, int b){
	return 1ll * a * b % mod;
}

const int N = 100010;

int n, a[N], b[N], trans[N], f[N];
int frac[N], Inv[N], finv[N];
bool vis[N];

inline int C(int n, int m){
	return mul(frac[n], mul(finv[m], finv[n - m]));
}

inline int calc(int n){
	if (f[n]) return f[n];
	int res = 0;
	for (int i = 0, mu = 1; i <= n / 2; mu = mul(mu, inv2), ++i){
		int z = C(n, i << 1);
		if (i > 0) z = mul(z, 2);
		z = mul(z, mul(frac[n], mu));
		res = add(res, z);
	}
	return f[n] = res;
}

signed main(){
	scanf("%d", &n);
	for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
	for (int i = 0; i < n; ++i) scanf("%d", &b[i]), trans[a[i] - 1] = b[i] - 1;
	frac[0] = 1, finv[0] = 1;
	for (int i = 1; i <= n; ++i) frac[i] = mul(frac[i - 1], i), Inv[i] = i == 1 ? 1 : mod - mul(mod / i, Inv[mod % i]), finv[i] = mul(finv[i - 1], Inv[i]);
	int res = 1;
	for (int i = 0, j, l = 0, r = n; i < n; ++i) if (!vis[i]){
		for (j = i, l = 0; !vis[j]; ++l, vis[j] = true, j = trans[j]);
		res = mul(mul(res, C(r, l)), calc(l)), r -= l;
	}
	printf("%d\n", res);
	return 0;
}
posted @ 2024-04-22 15:05  XuYueming  阅读(6)  评论(1编辑  收藏  举报