题意:
给你一个序列,每次从头到尾每一次让不为0的值减1,执行数次直到序列中所有数为0,问你满足序列中不会出现同一个数连续减两次的排列的种数。
思路:
易得:当序列中至少有一个最大数在所有比它小1的数的右边,那么该序列不满足条件,为了方便,考虑反面,用整个序列全排列的个数减去反面的个数。
考虑反面:当一个最大数在第n位时,这种情况的个数为: A n − 1 n − 1 ∗ A 最 大 数 个 数 最 大 数 个 数 ∗ A 比 最 大 数 小 一 的 数 的 个 数 比 最 大 数 个 数 小 一 的 数 的 个 数 A_{n-1}^{n-1}*A_{最大数个数}^{最大数个数}*A_{比最大数小一的数的个数}^{比最大数个数小一的数的个数} An−1n−1∗A最大数个数最大数个数∗A比最大数小一的数的个数比最大数个数小一的数的个数;
当一个最大数在第n-1位时,设a=n-比最大数小1的数的个数-最大数的个数的阶乘,这种情况的个数为: A a a ∗ A 最 大 数 个 数 最 大 数 个 数 ∗ A 比 最 大 数 小 一 的 数 的 个 数 比 最 大 数 个 数 小 一 的 数 的 个 数 A_a^a*A_{最大数个数}^{最大数个数}*A_{比最大数小一的数的个数}^{比最大数个数小一的数的个数} Aaa∗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;
}
}