C. Jury Meeting

题目链接
题意
给定一个长度为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;
 } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Think-killer

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值