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<<'
';
			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<<'
';

	}

    return 0;
}
原文地址:https://www.cnblogs.com/lr599909928/p/14158898.html