1. 程式人生 > 實用技巧 >Codeforces Round #690 (Div. 3) E2. Close Tuples (hard version) (數學,組合數)

Codeforces Round #690 (Div. 3) E2. Close Tuples (hard version) (數學,組合數)

  • 題意:給你一長度為\(n\)的序列(可能含有相等元素),你要找到\(m\)個位置不同的元素使得\(max(a_{i-1},a_{i_2},...,a_{i_m})-min(a_{i-1},a_{i_2},...,a_{i_m})\le k\),問你共有多少種不同的元祖滿足條件,對答案\(mod 1e9+7\).
  • 題解:我們可以先用map做桶統計每個數出現的次數,然後列舉\([1,n]\),用字首和\(pre\)統計出現的次數,然後我們再去列舉\([1,n]\),我們每次將\(i\)\([1,i-1]\)看成兩部分,從\(i\)\([1,i-1]\)中選數,這樣可以做到不重複不漏選,每次列舉從\(i\)
    中選的次數和\([1,i-1]\)選的次數求組合數即可.
  • 程式碼:
#include <bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define me memset
#define rep(a,b,c) for(int a=b;a<=c;++a)
#define per(a,b,c) for(int a=b;a>=c;--a)
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
using namespace std;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}

int t;
int n,m,k;
int a[N];
int f[N],inv[N];
int pre[N];
map<int,ll> mp;

int add(int x,int y){
	x+=y;
	if(x>=mod) x-=mod;
	return x;
}

int mul(int x,int y){
	return 1ll*x*y%mod;
}

int fpow(int a,int b){
	int res=1;
	while(b){
		if(b&1) res=mul(res,a);
		a=mul(a,a);
		b>>=1;
	}
	return res;
}

int C(int n, int m){
	if(n<m) return 0;
	return mul(f[n],mul(inv[n-m],inv[m]));
}

int main() {
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>t;

	f[0]=1;
	rep(i,1,N-1) f[i]=mul(f[i-1],i);
	inv[N-1]=fpow(f[N-1],mod-2);
	per(i,N-2,0) inv[i]=mul(inv[i+1],i+1);

	while(t--){
		cin>>n>>m>>k;

		mp.clear();

		rep(i,1,n){
			cin>>a[i];
			mp[a[i]]++;
		}

		if(m==1){
			cout<<n<<'\n';
			continue;
		}

		rep(i,1,n){
			pre[i]=pre[i-1]+mp[i];
		}
	
		ll ans=0;

		rep(i,1,n){
			int cur=mp[i];
			if(!cur) continue;
			int psum=pre[i-1]-((i-k-1>=0)?pre[i-k-1]:0);
			rep(j,1,min(cur,m)) ans=add(ans,mul(C(cur,j),C(psum,m-j)));
		}

		rep(i,1,n) pre[i]=0;

		cout<<ans<<'\n';

	}

    return 0;
}