C. Jury Meeting
题目
n个人发言,每个人都可以分一个次数,轮流发言,次数用尽就跳过,求有多少的分配排列可以使没有一个人连续发言。没有输出0.
思路
- 先考虑怎样的次数序列总不合法,对于序列最大值 m a x max max,如果它很大显然会有人连续发言,现在找到这个“大的界限”。
- 因为每一轮都会使发言次数减一,并且如果连续发言,此时一定只有一个次数非零的人,也就是说只要有一个人能充当“间隔” , 就不会使得人连续发言。
- 因此考虑最大值
a
a
a和第二大值
b
b
b的关系。
- 当 a = b a=b a=b时,显然在最后会是它们的交替,所有排列都合法。
- 当 a − b > 1 a-b>1 a−b>1时,总会出现连续,此时答案就为0
- 当 a − b = 1 a-b=1 a−b=1时,必须至少有一个b排在a的后面,详见下例。
/*
2
1 2
1 2 -> 122 ->no
2 1 -> 212 ->yes
*/
- 因此,现在只需要解决如何求最大值右边至少有一个次大值的排列数即可。
设次大值有 t o t tot tot个,考虑这 t o t + 1 tot+1 tot+1个数,右边一个次大值都没的可能性是 1 1 + t o t \frac{1}{1+tot} 1+tot1,所求概率为 t o t 1 + t o t \frac{tot}{1+tot} 1+tottot.
所以全排列乘上这个概率就行。
AC代码
时间复杂度: O ( n ) O(n) O(n)
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define debug(x) cout<<"> "<< x<<endl;
//#define int long long
using namespace std;
typedef pair<int,int> PII;
const int N =10 + 2e5 ,mod=998244353;
int n;
int a[N];
ll p[N];
ll ksm(ll _a,ll b)
{
ll res = 1 ;
while(b)
{
if(b&1) res=(res*_a)%mod;
_a=(_a*_a)%mod;
b>>=1;
// debug(res)
}
return res%mod;
}
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+1+n);
int tot = count(a+1,a+1+n,a[n]);
if(tot>=2)
cout<<p[n]<<endl;
else
{
tot = count(a+1,a+1+n,a[n]-1);
if(tot==0)
puts("0");
else
{
// tot/(tot+1) * x;
cout<<1ll*p[n]*tot%mod*ksm(tot+1,mod-2) %mod<<endl;
}
}
}
signed main()
{
ios::sync_with_stdio();cin.tie();cout.tie();
p[0]=1;
for(int i=1;i<N;i++) p[i] = p[i-1]*i%mod;
int T;cin>>T;
while(T--)
solve();
return 0;
}
后记
取模古古怪怪,要专门看看安全取模的技巧。还有排列组合,也感觉懵逼的