Codeforces Round #690 (Div. 3) E2 Close Tuples (hard version)

题意
给了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;
}

猜你喜欢

转载自blog.csdn.net/qq_43563669/article/details/111298514