1. 程式人生 > >BZOJ5322 JXOI2018 排序問題

BZOJ5322 JXOI2018 排序問題

傳送門

這是我見過的為數不多的良心九憐題。

題目大意

給定一個長度為$n$序列,你要在序列末尾加入$m$個$[L,R]$之間的數$m\leq 10^7,L,R\leq 10^9$,使得該序列猴子排序的輪數(一輪是指隨機打亂整個序列,不斷重複操作直到否有序)期望最大,求這個最大的期望。

 

題解

假設序列元素互不相同,那麼有序的排列方式只有一個,而排列方式的數量有$n!$種,每輪成功的概率是$\frac {1}{n!}$,所以期望輪數是$n!$。

考慮第$i$種元素有$cnt_i$個的序列有多少個。

元素是互不相同的,所以$cnt_i$個第$i$個元素在有序序列中的相對位置是固定的,而元素在這些位置的排列是任意的,所有有序的排列方式有$\prod cnt_i!$個,期望是$\frac {n!}{\prod cnt_i!}$。

若使上式子最大,由於$(n+1)!(n-1)!>(n!)^2$很明顯希望使得$\max cnt_i$最小且平均。

那麼我們統計原序列中$[L,R]$之間的數,每次加入數量最小的元素,直到加入了$m$個數,最後計算答案即可,這個排一下序優化一下就行了。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 200020
#define MAXN 10200010
#define mod 998244353
using namespace std;
namespace IO{
	const int BS=(1<<20); int Top=0;
	char Buffer[BS],OT[BS],*OS=OT,*HD,*TL,SS[20]; const char *fin=OT+BS-1;
	char Getchar(){if(HD==TL){TL=(HD=Buffer)+fread(Buffer,1,BS,stdin);} return (HD==TL)?EOF:*HD++;}
	void flush(){fwrite(OT,1,OS-OT,stdout);}
	void Putchar(char c){*OS++ =c;if(OS==fin)flush(),OS=OT;}
	void write(int x){
		if(!x){Putchar('0');return;} if(x<0) x=-x,Putchar('-');
		while(x) SS[++Top]=x%10,x/=10;
		while(Top) Putchar(SS[Top]+'0'),--Top;
	}
	int read(){
		int nm=0,fh=1; char cw=Getchar();
		for(;!isdigit(cw);cw=Getchar()) if(cw=='-') fh=-fh;
		for(;isdigit(cw);cw=Getchar()) nm=nm*10+(cw-'0');
		return nm*fh;
	}
}
using namespace IO;
int add(int x,int y){return (x+y>=mod)?x+y-mod:x+y;}
int mul(int x,int y){return (x==1||y==1)?x+y-1:(LL)x*(LL)y%mod;} 
int qpow(int x,int sq){
	int res=1;
	for(;sq;sq>>=1,x=mul(x,x)) if(sq&1) res=mul(res,x);
	return res;
}
int p[M],fac[MAXN],ifac[MAXN],cnt[M],t[M];
int main(){
	fac[0]=1;
	for(int i=1;i<MAXN;++i) fac[i]=mul(fac[i-1],i); ifac[MAXN-1]=qpow(fac[MAXN-1],mod-2);
	for(int i=MAXN-1;i;--i) ifac[i-1]=mul(ifac[i],i);
	for(int T=read();T;--T){
		int n=read(),m=read(),l=read(),r=read();
		int now=r-l+1,num=0,tot=1,ans,tmp=0,fin;
		for(int i=1;i<=n;i++) cnt[i]=0,p[i]=read();
		sort(p+1,p+n+1),ans=fac[n+m];
		for(int i=1;i<=n;i++,tot++){
			while(p[i]==p[i+1]&&i<n) i++,cnt[tot]++; cnt[tot]++;
			if(p[i]>=l&&p[i]<=r) t[++tmp]=cnt[tot],now--;
			else ans=mul(ans,ifac[cnt[tot]]);
		} tot--,sort(t+1,t+tmp+1);
		for(fin=1;fin<=tmp;fin++){
			if((LL)(t[fin]-num)*(LL)now>(LL)m) break;
			m-=(t[fin]-num)*now,now++,num=t[fin];
		} num+=m/now,m%=now;
		ans=mul(ans,mul(qpow(ifac[num+1],m),qpow(ifac[num],now-m)));
		for(int i=fin;i<=tmp;i++) ans=mul(ans,ifac[t[i]]);
		write(ans),Putchar('\n');
	}flush(); return 0;
}