CF 1569C Jury Meeting

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;
}
原文地址:https://www.cnblogs.com/unravel-CAT/p/15249628.html