链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
题目描述
给定一个长度为 n 的非负整数数组 a,保证 aaa 中元素至少有 n−1个数字互不相同。
求 a 的长度为 k 的不同子序列的方案数。
子序列:若序列 b可通过删除若干个 a中元素(可以删除0次)得到,则称 b 是 a的子序列。比如,[1,2],[1,3,2,4],[4] 都是 [1,3,2,4]的子序列,而 [3,1],[1,3,5]不是。
我们认为,如果两个子序列长度不同,或者长度相同但存在位置 i 使得两个子序列 b,c,成立,那么它们就是不同的。
由于答案可能很大,请输出对 1e9+7取模的结果。
输入描述
输出描述
示例
输入
3
4 4
1 2 3 4
4 4
1 1 3 4
4 3
1 1 4 514
输出
1
1
3
说明
前两个样例显然仅有一种方案。
对第三个样例,共[1,1,4],[1,1,514],[1,4,514]三种情况。
思路
首先想到就是求组合数,如果有n-1个数互不相同,那么有两个数是一样的,所以要减去重复情况,如果有n个数互不相同,那么没有相同的数,所以直接输出求得的组合数。
那么怎么求重复情况呢?
我开始想的是,重复情况肯定是出现了重复数的序列,于是我直接减去了C(n-2,k-1)——(从去除重复的2个数的序列中取k-1个)后来发现是不对的 ,我没有考虑顺序的问题(这是一个坑),这里并不是任取,而是删除其中若干个数,所以顺序是不会打乱的。正确思路如下:
假设一下n=7,k=5,序列为1,2,3,4,2,5,6(2重复)
我们要注意题目说的是删除序列中的若干是数使其长度为k,如果这里删除的是2,6,则这5个数是1,3, 4, 2 ,5,那么这里的2一定是第二个出现的2,不可能是第一个出现的2,所以这样是没有重复情况的,即取在相同的数第一个位置到第二个位置之间的数是不会有重复情况的。
所以重复的情况是C(n-len,k-1),这里len是相同的数第一个位置到第二个位置的长度。
注意这题要用到求逆元的知识。
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 1e9 + 7;
const int N = 1e5 + 5;
int jc[N];
inline int Mod(int u) {return (u%mod+mod)%mod;} //内联函数,取模,返回正数
int qkpow(int a,int p=mod-2)
{
int t=1,tt=a%mod;
while(p){
if(p&1) t=t*tt%mod;
tt=tt*tt%mod;
p>>=1;
}
return t;
}
int C(int a,int b) //求组合数
{
if(a<b)
return 0;
return Mod(jc[a]*qkpow(Mod(jc[b]*jc[a-b])));
}
void solve(){
int n,k;
cin>>n>>k;
map<int,int>mp;
int len=-1;
for(int i=1,x;i<=n;++i)
{
cin>>x;
if(!mp.count(x))
mp[x]=i;
else
len=i-mp[x]+1;
}
int cnt=C(n,k);
if(len!=-1)
cnt=Mod(cnt-C(n-len,k-1)); //减去重复的情况,k-1是因为已经确定了重复的那一位数
cout<<cnt<<"\n";
}
signed main() {
ios::sync_with_stdio(false), cin.tie(0);
jc[0] = 1;
for(int i=1;i<N;++i)
jc[i]=Mod(jc[i-1]*i); //预存,代表i的阶乘
int t;
cin>>t;
while(t--)
solve();
}