C. Jury Meeting(1569C)(组合数学)(正向思维)

C. Jury Meeting(1569C)(组合数学)(正向思维)


题目来源:C. Jury Meeting


题意:

有n个人,每个人的序号为1 ~ n,第 i 个人有 a[i] 个问题,每次一个人只能问一个问题

将这 n 个人排序,寻找有多少种序列使得不断从左到右提问题,不会出现同一个人连续问两次问题


思路:

首先通过样例我们不难发现,当最大的两个数之差小于等于1时答案不为0

  • 差大于 1 时,答案为 0
  • 差等于 0 时,答案为 n!
  • 差等于 1 时,记最大的两个数为 x 和 y,其中 x > y
    • 这时至少要有一个 y 排在 x 的后边才有解(参考样例 1 2),剩下的随便排列即可
    • 那么我们先统计出 y 的个数 num,这 num 个 y 随机排列,之后将 x 插空,由于至少有一个 y 在 x 后,所以此时相当于有 num 个空, 所以 ans * num! * num
    • 插入 x 后,有 num + 2 个空,之后每插入一个数,空隙就多一个,还剩余 n - num - 1 个数,那么也就是说答案再乘上 (num + 2) * (num + 3) * … * (num + 2 + n - num - 1) 即可

AC代码

#include <bits/stdc++.h>
#define endl "\n"
#define rep(i, m, n) for (int i = (m); i <= (n); ++i)
#define rrep(i, m, n) for (int i = (m); i >= (n); --i)
#define IOS ios::sync_with_stdio(0); cin.tie(0);
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 2e5 + 10, mod = 998244353;
int n, x[N];
ll f[N];//统计阶乘
void solve() {
	scanf("%d", &n);
	rep(i, 1, n) scanf("%d", &x[i]);
	sort(x + 1, x + 1 + n, greater<int>());//大 -> 小

	if (x[1] - x[2] > 1) { // 比较最大的两个数
		puts("0"); return;
	}

	ll ans = 1;
	if (x[1] == x[2]) ans = f[n]; //n!
	else {
		int num = 1, vis = 3;//num:第二大的数的个数
		//vis:统计之后插入n - num - 1个数时,空隙的个数
		rep(i, 3, n) //统计个数
			if (x[i] == x[2]) num++, vis++;
			else break;

		ans = f[num] * num % mod;
		while (n - num - 1) {
			ans = ans * vis % mod;
			n--; vis++;//插入一个数,空隙多一
		}
	}

	printf("%lld\n", ans);
}
int main() {
	f[0] = 1;
	rep(i, 1, n) f[i] = f[i - 1] * i % mod;
	int t; cin >> t;
	while (t--) solve();
	return 0;
}

END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值