CF-Jury Meeting

Jury Meeting

会有一个人连续说两个任务说明在某一轮中他是最后一个发言且除他之外的人都已经说完了所有任务,因此在下一轮中他成为了第一个发言的人。因此我们可以从所有人的任务数中的最大值的角度考虑这个问题。

  • 如果最大值的数量不唯一,则这些任务数量最大的人一定是在同一轮说完所有任务的,不会出现不可行的情况,则所有人可以任意排列组合,答案为\(n!\)

  • 如果最大值的数量唯一,且最大值与第二大值的差大于1,则无论如何排列,都会出现任务最多的人单独至少两轮,所以答案为0;

  • 如果最大值的数量唯一,且最大值与第二大值的差为1,则按如下方法考虑:
    直接求可行的排列较为复杂,可以通过间接的方法来求,求出全排列减去不可行的排列数量即为所求。
    设第二大值的人数为\(m\),如果出现任务最多的人连续讲两次的情况,说明他排在所有任务数第二多的人后面,则这样的排列有\(m!\)种,
    将剩下的人按照插空法排列进去,可知排列数为\(\frac{n!}{(m+1)!}\)
    所以不可行的排列数量为\(m!×\frac{n!}{(m+1)!}= \frac{n!}{m+1}\)
    因此可行的排列数量为\(n!−\frac{n!}{m+1}=\frac{m}{m+1}×n!\)

阶乘可以通过预处理得到,由于要取模,所以计算\(\frac{1}{m+1}\)时需要求逆元,可以用费马小定理和快速幂求得。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
using ll = long long;
const int N = 2e5 + 10;
const int MOD = 998244353;
ll T,n,a[N],res,av,bv;
ll fac[N];
void init(){
    fac[0] = 1;
    for(int i = 1; i <= 200010; ++i)
        fac[i] = (fac[i - 1] * (ll)i) % MOD;
}
ll qpw(ll a,ll k,ll p){
    ll res = 1 % p;
    while(k){
        if(k & 1)res = res * a % p;
        k >>= 1;
        a = a * a % p;
    }
    return res;
}
int main()
{
    cin>>T;
    init();
    while(T--){
        cin>>n;
        for(int i = 0; i < n; ++i) cin>>a[i];
        sort(a, a + n);
        av = a[n - 1], bv = a[n - 2];
        if(av == bv) res = fac[n];
        else if(av - 1> bv) res = 0;
        else if(bv == av - 1){
            int m = 0;
            for(int i = n - 2; i >= 0; --i) {
                if(a[i] == bv) m++;
                else break;
            }
            res = (m * qpw((m + 1), MOD - 2, MOD) % MOD * fac[n])% MOD;
        }
        cout<<res<<endl;
    }
    return 0;
}
posted @ 2021-10-17 20:15  Ivessas  阅读(61)  评论(0)    收藏  举报