problem
数据范围:
T
≤
1
0
5
T\leq 10^5
T≤105,
n
≤
2
×
1
0
5
n\leq 2\times 10^5
n≤2×105,
m
≤
1
0
7
m\leq 10^7
m≤107,
1
≤
l
≤
r
≤
1
0
9
1\leq l\leq r\leq 10^9
1≤l≤r≤109,
∑
n
≤
2
×
1
0
6
\sum n\le 2\times 10^6
∑n≤2×106,其中
T
T
T 表示数据组数。
solution
首先有一个比较显然的结论,即 a n s = n ! ∏ c i ! ans=\frac{n!}{\prod c_i!} ans=∏ci!n!,其中 c i c_i ci 是数列 { a n } \{a_n\} {an} 中 i i i 的出现次数。其实就是可重排列方案数。
为了最大化 a n s ans ans,我们就应该最小化 ∏ c i ! \prod c_i! ∏ci!。
那么就得到了一个暴力的想法,我们每次在 [ l , r ] [l,r] [l,r] 的 c i c_i ci 找一个最小的,把 i i i 填进去,这样肯定是最优的。
但是由于 m m m 很大,不能直接这样做,得换种思路。
把 [ l , r ] [l,r] [l,r] 原来的 c i c_i ci 和原 c i c_i ci 的个数记下来,按照 c i c_i ci 排个序,按照试填法的思想来填,如果当前填不满特判一下能填到哪。
感觉口述不太清楚,可以看看代码,或者自己好好推一推,应该挺好理解的。
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5,M=2e7+5,P=998244353;
int T,n,m,l,r,tot,cnt,a[N],b[N],fac[M];
int add(int x,int y) {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y) {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y) {return 1ll*x*y%P;}
int power(int a,int b){
int ans=1;
for(;b;b>>=1,a=mul(a,a)) if(b&1) ans=mul(ans,a);
return ans;
}
void prework(){
fac[0]=fac[1]=1;
for(int i=2;i<M;++i) fac[i]=mul(fac[i-1],i);
}
int main(){
prework();
scanf("%d",&T);
while(T--){
scanf("%d%d%d%d",&n,&m,&l,&r);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
sort(a+1,a+n+1),tot=cnt=0;
int ans=1,sum=r-l+1;
for(int i=1,j=1;i<=n;i=j){
while(j<=n&&a[j]==a[i]) ++j;
if(a[i]<l||a[i]>r) ans=mul(ans,fac[j-i]);
else b[++tot]=j-i,sum--;
}
sort(b+1,b+tot+1);
for(int i=1,j=1;i<=tot;i=j){
while(j<=tot&&b[j]==b[i]) ++j;
a[++cnt]=b[i],b[cnt]=j-i;
}
int lim=m;
a[++cnt]=2e9,b[cnt]=1;
for(int i=1;i<=cnt;++i){
ll num=1ll*(a[i]-a[i-1])*sum;
if(num>lim){
int Div=a[i-1]+lim/sum,Mod=lim%sum;
for(int j=i;j<cnt;++j) ans=mul(ans,power(fac[a[j]],b[j]));
ans=mul(ans,mul(power(fac[Div],sum-Mod),power(fac[Div+1],Mod)));
break;
}
else lim-=num,sum+=b[i];
}
printf("%d\n",mul(fac[n+m],power(ans,P-2)));
}
return 0;
}