题目链接
题意
给定一个长度为n的数组,可以进行任意排序(n!种排序),从左到右每个数减1,如果减为0则跳过,循环直至所有都为0
求不存在某个数出现连续减2次的排列组合个数
可以发现只需关注最大值a和次大值b的关系,因为小于他们的数都会在某次循环中减为0,且每次循环至少在最大值a处间隔开,因此其它数不可能连续减两次
1:取出数组中的最大值a和次大值b
情况一:如果a=b时 a b循环的次数同样多,同时减为0,答案为n!。
情况二:如果a=b+1时 当a=2,b=1时,只有顺序{2,1}才可以,因此只需数组中的a后面至少有一个b即可,n!种排序,减去不符合的情况即可,
求不符合的情况:
1:分别求出最大值的个数maxx1,次大值的个数maxx2,其余的数个数mix
2:取出一个最大值c[maxx1][1]分别放在数组n~~1的位置,最大值的后面还剩i(0~n)个位置,
3:当最大值后面还剩i个位置时 减去不符合的数为
c[maxx1][1]
∗
\ast
∗c[mix][i]
∗
\ast
∗i!
∗
\ast
∗(n-i-1)!
情况三: 如果a>=b+1 可知都不符合
#include<iostream>
#include<algorithm>
#include<cstring
#include<vector>
using namespace std;
typedef long long LL;
const int N=200010 ,mod=998244353;
int n;
int a[N];
LL P[N]; //i的阶乘P[i]
int max1,max2;
int fact[N], infact[N]; 预处理/逆元(nlogn)
//快速幂
LL qmi(int a, int k, int p)
{
LL res = 1;
while (k)
{
if (k & 1) res = (LL)res * a % p;
a = (LL)a * a % p;
k >>= 1;
}
return res;
}
//求组合数
int c(int a,int b)
{
return (LL)fact[a] * infact[b] % mod * infact[a - b] % mod;
}
LL solve()
{
//分别求出最大值的个数maxx1,次大值的个数maxx2,其余的数的个数mix
int mix=0,maxx1=0,maxx2=0;
for(int i=0;i<n;i++)
if(a[i]<max2) mix++;
else if(a[i]==max2) maxx2++;
else maxx1++;
int res=P[n]; //n的阶乘
for(int i=0;i<=mix;i++)
{
//不符合的方式:c[maxx1][1]*c[mix][i]*i!*(n-i-1)!
LL sum=1ll*c(maxx1,1)*c(mix,i)%mod*P[i]%mod*P[n-i-1]%mod;
res=((res-sum)%mod+mod)%mod;
}
return res;
}
int main()
{
int t;
scanf("%d",&t);
//预处理阶乘
P[0]=1;
for(int i=1;i<N;i++)
P[i]=(LL)P[i-1]*i%mod;
//预处理/逆元(nlogn)
fact[0] = infact[0] = 1;
for (int i = 1; i < N; i ++ )
{
fact[i] = (LL)fact[i - 1] * i % mod;
infact[i] = (LL)infact[i - 1] * qmi(i, mod - 2, mod) % mod;
}
while(t--)
{
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
sort(a,a+n);
//寻找最大值和次大值
max1=a[n-1],max2=a[0];
for(int i=n-2;i>=0;i--)
if(a[i]!=max1)
{
max2=a[n-2];break;
}
if(max1==max2) printf("%d\n",P[n]);
else if(max1==max2+1) printf("%d\n",solve());
else printf("0\n");
}
return 0;
}
将公式 c[maxx1][1]
∗
\ast
∗c[mix][i]
∗
\ast
∗i!
∗
\ast
∗(n-i-1)!优化为
maxx1
∗
\ast
∗A[mix][i]
∗
\ast
∗(n-i-1)!
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long LL;
const int N=200010 ,mod=998244353;
int n;
int a[N];
LL P[N]; //i的阶乘P[i]
int max1,max2;
void init()
{
//预处理阶乘
P[0]=1;
for(int i=1;i<N;i++)
P[i]=(LL)P[i-1]*i%mod;
}
LL solve()
{
//分别求出最大值的个数maxx1,次大值的个数maxx2,其余的数的个数mix
int mix=0,maxx1=0,maxx2=0;
for(int i=0;i<n;i++)
if(a[i]<max2) mix++;
else if(a[i]==max2) maxx2++;
else maxx1++;
int res=P[n]; //n的阶乘
LL A=1; //排列数A[mix][0]
for(int i=0;i<=mix;i++)
{
//不符合的方式:c[maxx1][1]*c[mix][i]*i!*(n-i-1)!
// maxx1*A[mix][i]*(n-i-1)!
LL sum=1ll*maxx1*A%mod*P[n-i-1]%mod;
res=((res-sum)%mod+mod)%mod;
A=A*(mix-i)%mod; //A=A[mix][i+1];
}
return res;
}
int main()
{
int t;
scanf("%d",&t);
init();
while(t--)
{
scanf("%d",&n);
for(int i=0;i<n;i++) scanf("%d",&a[i]);
sort(a,a+n);
//寻找最大值和次大值
max1=a[n-1],max2=a[0];
for(int i=n-2;i>=0;i--)
if(a[i]!=max1)
{
max2=a[n-2];break;
}
if(max1==max2) printf("%d\n",P[n]);
else if(max1==max2+1) printf("%d\n",solve());
else printf("0\n");
}
return 0;
}