题意:
给了n个数字,范围是1~n,数字可能存在重复的。
现在给你一个k和m,问你有多少可以选择方案,选择m个不同下标的元素,让他们最大值减去最小值的差小于等于k
E1与E2的区别在于,E1 k=2,m=3 并且不需要取模
思路:
① 考虑一下,我们只需要枚举最大值,然后计算出第一个满足的最小值,看这个区间有多少个数字,然后选择m个即可。所以我们对序列进行从小到大排序,从第m个数到第n个数遍历一下,都作为序列的最大值。
② 我们可以通过二分计算出第一个大于等于a[i]-k的位置pos,然后我们可以算出从pos到当前位置i,区间长度num为i-pos+1
③ 我们要选择m个数字,这里注意一下,对于当前位置我们一定要选上,所以方案数就是C(num-1,m-1)。所有方案数累加就是答案了。
④ 对于E2我们可以提前处理好阶乘然后每次计算复杂度都是log的。
对于E1的话,不需要取模,又因为m=2 所以计算的就是
C(num-1,2) =num-1) × (num-2) / 2
E1代码就不贴了。
E2代码如下
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+50;
const ll mod=1e9+7;
ll a[N],f[N];
ll qpow(ll a,ll b){
ll ans=1;
a%=mod;
while(b){
if(b&1) ans=ans*a%mod;
b>>=1;
a=a*a%mod;
}
return ans;
}
ll C(int n,int m){
if(n<m) return 0;
return f[n]*qpow(f[m]*f[n-m],mod-2);
}
int main(){
f[0]=1;
for(int i=1;i<N;i++) f[i]=f[i-1]*i%mod;
int T;cin>>T;
while(T--){
int n,m,k;cin>>n>>m>>k;
for(int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+n+1);
ll ans=0;
for(int i=m;i<=n;i++){
int pos=lower_bound(a+1,a+n+1,a[i]-k)-a;
int num=i-pos+1;
ans+=C(num-1,m-1);
ans%=mod;
}
cout<<ans<<endl;
}
return 0;
}