Educational Codeforces Round 113C Jury Meeting(求组合数)

题目大意:
给定一个数组,从左往右对数组中每个数减1,如果当前数为0就不对其操作
对这个数组有多少种排列方式,使得一个数不会被连续减两次
分类讨论:
1:如果数组中最大的数远远大于其他数(最大数–次大数>=2),就没有任意一种排列方式满足题意
2:如果数组中的最大数>=2(最大数不止一个)那么无论怎么排列都是满足题意的,答案就是数组数量的阶乘
3:存在一个最大数,至少存在一个次大数使得最大数-次大数==1
此时判断有哪些非法排列情况:显然,当次大数全部位于最大数的左边时,该排列是非法的
不理解的可以用 1 2 2 2 3 1 来模拟一下
此时就可以枚举最大数左边的空间(最大数的位置),来计算所有次大数位于最大数左边的情况下排列的数量
设当前最大数左边的空间是i,一共有n个数,次大数有num2个
如果i<num2,continue
此时,先对最大数的左右两边进行阶乘,计算排列

(i)!*(n-i-1)!

显然,非法的排列情况不止这些
由于次大数的位置被固定在最大数左边,还要考虑小于次大数的数(普通数)的分配问题:这些普通数一些被分配在左边,一些在右边,被分配在左边的数量为i-num2,一共有n-1-num2个普通数,这就变成一个组合问题了,预处理逆元,快速求出组合数,所以完整的式子是:

(i)!*(n-i-1)!*C(n - 1 - num2,i - num2)

求组合数的时候傻乎乎地去copy了一份lucas来求组合数,TLE了,我是傻逼QWQ

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 998244353;
const int N = 2e5 + 10;
int a[N];
int n;
vector<int>g;
int ini[N];
int fact[N], infact[N];
bool cmp(int a, int b) {
    
    
	return a > b;
}
int qmi(int a, int k, int p) {
    
    
	int res = 1;
	while (k) {
    
    
		if (k & 1)
			res = res * a % p;
		a = a * a % p;
		k >>= 1;
	}
	return res;
}

void init() {
    
    
	int k = 2e5;
	ini[1] = ini[0] = 1;
	for (int i = 2; i <= k; i++) {
    
    
		ini[i] = (ini[i - 1] * i) % mod;
	}
	fact[0] = infact[0] = 1;
	for (int i = 1; i < N; i ++ ) {
    
    
		fact[i] = fact[i - 1] * i % mod;
		infact[i] = infact[i - 1] * qmi(i, mod - 2, mod) % mod;
	}

}



int get(int a, int b, int p) {
    
    //求组合数
	return fact[a] * infact[b] % p * infact[a - b] % p;
}


void solve() {
    
    

	cin >> n;
	g.clear();
	for (int i = 1; i <= n; i++) {
    
    
		cin >> a[i];
		g.push_back(a[i]);
	}
	sort(g.begin(), g.end(), cmp);
	if (g[0] - g[1] >= 2) {
    
    
		cout << "0" << endl;
		return;
	}
	int num1 = 0;
	int num2 = 0;
	for (auto i : g) {
    
    
		if (i == g[0])
			num1++;
		if (i == g[0] - 1)
			num2++;
	}
	if (num1 >= 2) {
    
    
		cout << ini[n] << endl;
		return;
	}
	int res = ini[n];
	int del = 0;
	for (int i = 1; i <= n - 1; i++) {
    
    
		if (i < num2)
			continue;//
		else {
    
    
			del = (del + ((ini[i] * get(n - 1 - num2, i - num2, mod) % mod) * ini[n - i - 1] ) % mod) % mod;
		}
	}
	res = ((res  - del) + mod) % mod;
	cout << res << endl;
}

signed main() {
    
    
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	init();
	int t;
	cin >> t;
	while (t--) {
    
    
		solve();
	}
}

おすすめ

転載: blog.csdn.net/fdxgcw/article/details/120226016