Educational Codeforces Round 113 (Rated for Div. 2) C. Jury Meeting

88 阅读1分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。 传送门

题意:

给你一个序列,每次从头到尾每一次让不为0的值减1,执行数次直到序列中所有数为0,问你满足序列中不会出现同一个数连续减两次的排列的种数。

思路:

易得:当序列中至少有一个最大数在所有比它小1的数的右边,那么该序列不满足条件,为了方便,考虑反面,用整个序列全排列的个数减去反面的个数。 考虑反面:当一个最大数在第n位时,这种情况的个数为:An1n1A最大数个数最大数个数A比最大数小一的数的个数比最大数个数小一的数的个数A_{n-1}^{n-1}*A_{最大数个数}^{最大数个数}*A_{比最大数小一的数的个数}^{比最大数个数小一的数的个数}; 当一个最大数在第n-1位时,设a=n-比最大数小1的数的个数-最大数的个数的阶乘,这种情况的个数为:AaaA最大数个数最大数个数A比最大数小一的数的个数比最大数个数小一的数的个数A_a^a*A_{最大数个数}^{最大数个数}*A_{比最大数小一的数的个数}^{比最大数个数小一的数的个数} ......以此类推。

#include<bits/stdc++.h>
#include<ctime>
using namespace std;
#define ll long long 
const int mod = 998244353;
ll a[200010];
ll sum[200010];
ll A[200010];
map<ll,ll>vis;
long long qpow(long long c, long long b)
{
	long long sum = 1;
	while (b) {
		if (b & 1) {
			sum = (sum * c) % mod;
			b--;
		}
		b /= 2;
		c = c * c % mod;
	}
	return sum%mod;
}
int main()
{
	int t;
	cin>>t;
	while(t--)
	{
		memset(sum,0,sizeof(sum));
		vis.clear();
		int n;
		cin>>n;
		ll maxx = 0;
		int flag = 1;
		for(int i = 1; i <= n; i++)
		{
			cin>>a[i];
			vis[a[i]]++;
			if(i > 1)
			{
				if(maxx == a[i] && flag)flag = 1;
				else flag = 0;
			}
			maxx = max(maxx, a[i]);
		}
		ll ans = 1;
		for(int i = 2; i <= n; i++)ans*=i, ans%=mod;
		if(flag)
		{
			cout<<ans%mod<<endl;
			continue;
		}
		if(vis[maxx] > 1)
		{
			cout<<ans%mod<<endl;
			continue;
		}
		ll res = 0;
		if(vis[maxx-1])
		{
			ll num = vis[maxx-1];
			ll maxx_sum = vis[maxx];
			A[0] = 1;
			for(int i = 1; i <= n; i++)A[i] = A[i-1]*i%mod;
			ll last = n-maxx_sum-num;
			sum[0] = 1;
			ll now = last;
			for(int i = 1; i <= last; i++)
			{
				sum[i] = sum[i-1]*now%mod;
				now--;
			}
			ll res = 0;
			for(int i = n; i >= num+vis[maxx]; i--)
			{
				res += sum[n-i]%mod*A[i-1]%mod*A[maxx_sum]%mod;
				res%=mod;
			}
			cout<<(ans+mod-res)%mod<<endl;
		}
		else cout<<0<<endl;
	}
}