返回顶部

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;
}
posted @ 2020-12-19 14:08  Rayotaku  阅读(115)  评论(0编辑  收藏  举报