E2. Close Tuples (hard version)
阿新 • • 發佈:2020-12-21
本題為hard版,還有一個easy版,區別在於k和m的取值不同。
題意:
給出一個由n個數字組成的陣列 \(a\)。現在定義一種子集為\(\{A_1, A_2, A_3, ..., A_m\}\),使得這個子集中的最大值和最小值的差值不超過k,其中m和k是給出的。現在問你這種子集有幾個。
思路:
對給出的陣列進行排序,用\(for\)迴圈列舉子集中的\(A_1\),之後用upper_bound找到陣列中第一個數字,使得這個數字減去\(A_1\)大於k。若這個數字和\(A_1\)之間元素的個數大於\(m - 1\),那麼利用組合數\(C_n^m\)就可以求出部分答案。最後將每一部分的答案加起來就可以得到最終答案。
額外知識:
由於題目要求取模,但是組合數中卻有除法,不能直接取模,所以需要用到逆元。 組合數取模板子
AC程式碼(直接用板子):
#include <cstdio> #include <algorithm> #include <iostream> const int N = 2e5 + 5; const int mod = 1e9 + 7; typedef long long ll; ll f[N]; ll qpow(ll a, ll b) { ll ans = 1, base = a; while (b) { if (b & 1) ans = ans * base % mod; base = base * base % mod; b >>= 1; } return ans; } void init() { f[0] = 1; for (int i = 1; i <= 2e5; i++) { f[i] = f[i - 1] * i % mod; } } ll cal(ll n, ll m) { if (n < m) return 0; return 1ll * f[n] * qpow(f[m], mod - 2) % mod * qpow(f[n - m], mod - 2) % mod; } int a[N]; int main () { init(); int T, n, m, k; scanf ("%d", &T); while (T--) { scanf ("%d %d %d", &n, &m, &k); for (int i = 0; i < n; i++) { scanf ("%d", &a[i]); } std::sort (a, a + n); int p = 0; ll ans = 0; for (int i = 0; i < n; i++) { p = (int)(std::upper_bound(a, a + n, a[i] + k) - a); if (p - i >= m) { ans = ans + cal(p - i - 1, m - 1); ans = ans % mod; } } printf ("%lld\n", ans); } return 0; }