链接:https://ac.nowcoder.com/acm/contest/283/B
来源:牛客网
不吉利的数
时间限制:C/C++ 2秒,其他语言4秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
题目描述
在数学中,某个序列的子序列是从最初序列通过去除某些元素但不破坏余下元素的相对位置(在前或在后)而形成的新序列。
假设有一条数列。可以在里面抽出指定的项组成新的子数列。
注意:子数列的次序必须和主数列的次序一样。
例如:
,只抽出双数项,就会有子数列。。
引用自 子序列——维基百科
在中国文化里,4和“死”谐音,7和“气”谐音,都是不吉利的含义。现在我们定义所有只由4和7组成的数字为不吉利的数字,例如4、47、74、44、7774等。
现在有一个数列array, 元素个数为 n ,给定一个数 m ,你可以在array任意选取 m 个数字的子序列,对于任何一个子序列只要有任意位置下标不同则视为不同的子序列。
例如选择下标为与下标为的元素作为子序列,则是不同的子序列,不论元素集合是否相同。
现在要求选取的数字中同一个不吉利的数最多出现一次,请问有多少种选法?
答案可能很大,请输出答案对取模之后的结果。
输入描述:
第一行为两个整数:n和m
接下来一行为 n 个整数,其中第i个表示array[i]
所有数据满足:
输出描述:
在一行内输出一个整数表示答案
示例1
输入
4 3 2 44 44 477
输出
2
说明
所有选择方法中选择下标为(1,2,4)和(1,3,4)的子序列满足要求
思路:
找到重复的含4和7且不含其他数字的数,然后只保留一个这个数,然后用组合数求出有多少种方法,乘以所有有过重复的数,然后用快速幂,和除法取模去算出最后的答案。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
const int maxn=200000+10;
ll a[maxn];
map<int,int>mp;
ll ksm(ll a,ll b)
{
long long res=1;
a=a%mod;
while(b)
{
if(b&1)
{
res=res*a%mod;
}
b/=2;
a=a*a%mod;
}
return res;
}
int main()
{
ll n,m;
scanf("%lld%lld",&n,&m);
int sum=0;
for (int i=0;i<n;i++)
{
int flag=0;
scanf("%d",&a[i]);
ll y=a[i];
while(a[i])
{
int x=a[i]%10;
if(x!=4&&x!=7)
flag=1;
a[i]=a[i]/10;
}
if(!flag)
{
if(mp[y]!=0)
{
sum++;
}
mp[y]++;
}
}
ll ans1,ans2=n-sum;
if(ans2==m)
ans1=1;
else if(ans2<m)
ans1=0;
else
{
ll cnt1=1,cnt2=1;
for(int i=1;i<=ans2;i++)
{
cnt1=cnt1*i%mod;
}
for(int i=1;i<=m;i++)
{
cnt2=cnt2*i%mod;
}
for(int i=1;i<=ans2-m;i++)
{
cnt2=cnt2*i%mod;
}
ans1=cnt1*ksm(cnt2,mod-2)%mod;
}
for (auto j=mp.begin();j!=mp.end();j++)
{
ans1=ans1*j->second%mod;
}
cout<<ans1<<endl;
}