C.Jury Meeting
来源:https://codeforces.com/contest/1569/problem/C
标签:【组合数】【数学】
题目简述
n个人参加聚会,每个人有ai个问题,聚会上大家轮流讲述自己的问题,当前人讲完一个自己的问题后轮到下一位,并且当前人问题数减一。问题讲述完者自动出局。聚会持续到直到所有人讲完问题。询问有多少种排列满足如下条件:
聚会过程中没有任何一个人能连续讲述自己的两个问题。
由于方案数过大,对998244353取模。
Input
第一行输入测试样例数T(1<=t<=104);
对于每一个测试样例,第一行输入成员数 n (2<=n<=2*105),接下来一行n个数ai,代表每一个成员的问题数.
Output
每一个测试样例输出一个答案。
Sample Input
4
2
1 2
3
5 5 5
4
1 3 3 7
6
3 4 2 1 3 3
Sample Output
1
6
0
540
More Info
对于第一个样例,[1,2]不可行,因为第二位会连续讲述两个自己的问题,[2,1]可行,所以答案为1.
题目思路
首先要明白一个结论,只有问题最多(>=2)的人排在最后且这样的人只有一位的情况下,才可能出现有人连续讲述两个问题的情况,而这个人就是他自己。
设最大数为mx,次大数(即最大数-1)为nmx,最大数的个数为mxcnt,次大数的个数为nmxcnt,默认mx>=2。
(1)当mxcnt>=2的时候,答案为全排列,ans=n!;
(2)当mxcnt=1且nmxcnt=0的时候,无解,ans=0;
(3)当mcnt=1且nmxcnt>0的时候,只有所有次大数排在最大数前面的排列不满足要求,因为这样的排列最终都会形成[1,2]的局面,用全排列减去这些排列的数目,就是答案。(也可以参考下面的写法,求把最大数放在所有次大数的后面的排列)
代码(附注释)
#include <iostream>
#include <algorithm>
using namespace std;
#define endl '
';
#define ll long long
const int N=200005;
const int mod=998244353;
int t,n,a[N];
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin>>t;
while(t--){
cin>>n;
int mx=0,mcnt=0;
for(int i=1;i<=n;++i) cin>>a[i],mx=max(mx,a[i]);
for(int i=1;i<=n;++i) if(a[i]==mx) mcnt++;
int p=1,b=1;
ll ans=1;
if(mcnt==1){//最大数数目为1
mcnt=0;
for(int i=1;i<=n;++i) if(a[i]==mx-1) mcnt++;
p=mcnt+1,b=mcnt;
}
for(int i=1;i<=n;++i){
if(i==p) ans=(ans*b)%mod;
else ans=(ans*i)%mod;
}
cout<<ans<<endl;
}
return 0;
}