继续来数数

题目链接:继续来数数

考虑 n 个数都互不相同的情况,此时任意拿出一个子序列都不会发生重复,答案就是一个组合数:Cn k。

考虑有 n−1个数互不相同的情况,那么会出现一种重复的情况:两个相同的数选其一,并且它们之间不再选其他数字。

比如:[1,2,3,4,5,3,6]。如果我们选出一个子序列 [1,3] ,此时选的是第一个三还是第二个三就不确定,出现重复,如果是 [1,3,4] 我们就可以定位到此时选了第一个三。

因此,在求出 Cnk​ 总的方案数,减去重复情况:重复数字选其一,然后在除了重复数字之间的数中把剩下 k−1 个选完,也就是 Cn−1−(possecond−posfirst) k−1

需要注意要保证 k−1≤n−1−possecond+posfirst,否则不可能出现重复(子序列太长,一定会选至少一个相同数之间的元素)。

代码:
 

#include <bits/stdc++.h>
#define int long long
#define fi first
#define se second
using namespace std;
const int inf = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9+7;
const int N = 1e5+5;
int n,k;
int a[N];
map<int,int>mp;
int fact[N],infact[N];

int qmi(int a,int b,int mod){
    int res = 1;
    while(b){
        if(b&1)res=(res*a)%mod;
        b>>=1;
        a = (a*a)%mod;
    }
    return res;
}

int C(int a,int b){
    return fact[a]*infact[b]%mod*infact[a-b]%mod;
}

void solve(){
	cin>>n>>k;
    mp.clear();
    for(int i=1;i<=n;i++)cin>>a[i];

    if(n==k){
        cout<<1<<"\n";
        return;
    }

    int ans = C(n,k);

    for(int i=1;i<=n;i++){
        if(!mp[a[i]]){
            mp[a[i]] = i;
        }
        else{
            if(n-1-(i-mp[a[i]])>=k-1)ans = (ans- C(n-1-(i-mp[a[i]]),k-1)+mod)%mod;
            mp[a[i]] = i;
        }
    }

    cout<<ans<<"\n";

}

signed main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);

    fact[0]=infact[0]=1;
	for(int i=1;i<N;i++){
		fact[i]=fact[i-1]*i%mod;
		infact[i]=infact[i-1]*qmi(i,mod-2,mod)%mod;
	}

    int t=1;cin>>t;
    while(t--){
        solve();
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值