Codeforces Round #690 (Div. 3) E2. Close Tuples (hard version) (數學,組合數)
阿新 • • 發佈:2020-12-19
- 題意:給你一長度為\(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\)
- 程式碼:
#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; }