【训练题20:组合数学】E2. Close Tuples | CF 690 (Div 3)

E2. Close Tuples (hard version)

难度比

− 1612 13570 -\frac{1612}{13570} 135701612

以 后 这 些 题 目 用 :   − 过 这 题 的 人 数 过 这 场 签 到 题 的 人 数     表 示 难 度 比   越 接 近 0 越 难 , 加 了 负 号 , 所 以 仍 然 是 越 大 越 难 \color{red}以后这些题目用:\\\ \\- \frac{过这题的人数}{过这场签到题的人数}\ \ \ 表示难度比\\\ \\越接近0越难,加了负号,所以仍然是越大越难      0
当时只写出了Easy版本,现在补Hard的

题意

给你一个长度为 n n n 的数组 a [   ] = { a 1 ⋯ a n } a[\ ]=\{a_1\cdots a_n\} a[ ]={ a1an}
让你找出所有的 m m m 元组,并且使得选出的这 m m m 个数字的 极差 ≤ k \le k k

更正式的表示:
找到 i 1 < i 2 < ⋯ < i m i_1<i_2<\cdots<i_m i1<i2<<im,s.t. max ⁡ a j − min ⁡ a j ≤ k \max a_j - \min a_j \le k maxajminajk, 其中 j = { i 1 , ⋯   , i m } j=\{i_1,\cdots,i_m\} j={ i1,,im}

问你,这样的 m m m 元组的数量,取模 1 e 9 + 7 1e9+7 1e9+7

数据范围

1 ≤ n ≤ 2 × 1 0 5 1\le n\le 2\times10^5 1n2×105
1 ≤ m ≤ 100 1\le m\le 100 1m100
1 ≤ k ≤ n 1\le k\le n 1kn
1 ≤ a i ≤ n 1\le a_i\le n 1ain

思路

  • 首先是一个非常重要的结论:原来的数组 a a a 任意元素交换顺序,答案不会影响。因为求 m m m 元组的本质就是找到所有不同的集合大小为 m m m 的符合条件的子集个数
  • 那么我们选择对原来的数组进行升序排序,答案自然就不会改变。
  • 然后,如果我们选择了 a i a_i ai 这个元素作为你所选择的 m m m元组的最小值,那么剩下来 m − 1 m-1 m1 个数字要在后续的数字中符合条件的范围内,即范围 [ a i , a i + k ] [a_i,a_i+k] [ai,ai+k] 的个数 c n t − 1 cnt-1 cnt1 ( 去 掉 了 a i 自 己 \color{red}去掉了a_i自己 ai)里面选择 m − 1 m-1 m1个,易得答案为 C c n t − 1 m − 1 C_{cnt-1}^{m-1} Ccnt1m1
  • 答案即为所有位置的累加,即 ∑ C c n t − 1 m − 1 \sum C_{cnt-1}^{m-1} Ccnt1m1
  • 那么我们只要预处理阶乘和阶乘的逆元即可。
  • 计算范围边界可以使用 l o w e r _ b o u n d lower\_bound lower_bound函数或者尺取。

题外话

  • 为什么对于位置 i i i ,我们不在所有范围 [ a i , a i + k ] [a_i,a_i+k] [ai,ai+k] 中选择 m m m 个,即答案为 ∑ C c n t m \sum C_{cnt}^{m} Ccntm 呢?
  • 举例:(数组已经升序后)假设 a 1 ∼ a 10 a_1\sim a_{10} a1a10 的极差为 k k k , a 5 ∼ a 2 0 a_5\sim a_20 a5a20 的极差也为 k k k
  • 那么,容易想到 a 6 , a 7 , a 8 {a_6,a_7,a_8} a6,a7,a8这个三元集在两个范围内都被枚举到了。
  • 所以,我们要固定这个 m m m 元集中 a i a_i ai 是一定要选择的最小元素进行枚举,就不会计算重复。

核心代码

时间复杂度 O ( 2 e 5 + ∑ n ) O(2e5+\sum n) O(2e5+n)

/*
 _            __   __          _          _
| |           \ \ / /         | |        (_)
| |__  _   _   \ V /__ _ _ __ | |     ___ _
| '_ \| | | |   \ // _` | '_ \| |    / _ \ |
| |_) | |_| |   | | (_| | | | | |___|  __/ |
|_.__/ \__, |   \_/\__,_|_| |_\_____/\___|_|
        __/ |
       |___/
*/
const int MAX = 2e5+50;

int aa[MAX];
ll fac[MAX],infac[MAX];
void init(int x){
    
    
    fac[0]   = 1;
    infac[0] = 1;
    for(int i = 1;i <= x;++i)
        fac[i] = (fac[i-1] * i) % MOD;
    infac[x] = inv(fac[x]);
    for(int i = x - 1;i >= 1;--i)
        infac[i] = (infac[i + 1] * (i + 1)) % MOD;
}
ll C(ll n,ll m){
    
    
    if(m > n)return 0;
    ll ans = fac[n] * infac[m] % MOD * infac[n - m] % MOD;
    return ans;
}
int main()
{
    
    
    int T;scanf("%d",&T);
    init(200000);
    while(T--){
    
    
        int n,m,k;
        scanf("%d%d%d",&n,&m,&k);
        for(int i = 0;i < n;++i)scanf("%d",&aa[i]);
        sort(aa,aa+n);
        int ed = -1;
        ll ans = 0;
        for(int i = 0;i < n;++i){
    
    
            while(ed + 1 < n && aa[ed+1] - aa[i] <= k)ed++;
            ans += C(ed - i,m - 1);
            ans %= MOD;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45775438/article/details/111273254
今日推荐