“东信杯”广西大学第一届程序设计竞赛(同步赛) B 不吉利的数 【规律 逆元 排列组合】

传送门:https://ac.nowcoder.com/acm/contest/283/B

题目描述 

数学中,某个序列的子序列是从最初序列通过去除某些元素但不破坏余下元素的相对位置(在前或在后)而形成的新序列。

假设有一条数列。可以在里面抽出指定的项组成新的子数列

注意:子数列的次序必须和主数列的次序一样。

例如:

,只抽出双数项,就会有子数列。

 
引用自 子序列——维基百科
在中国文化里,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)的子序列满足要求

题意概括:如题

解题思路:

记原数列 长度为 N

记录 每种不吉利数的个数,如果该类不吉利数只有一个那就保留在原数列中,否则把其余的都从原序列去掉。

记去掉重复不吉利数的序列长为 Slen

那么 剩下的序列里选 M 个 C( Slen, M) 这些是满足条件的子序列,但不是全部;

因为下标不同,子序列也不同。

所以我们刚才记录每种不吉利数 的个数就用上了,有多少种不吉利数就有多少个格子,而这些格子里的那个不吉利数可以替换成同种类型但是不同下标的。

举个栗子:

6 3

2 4 4 7 7 747

不吉利数 4 : 2个 

不吉利数 7:2个

不吉利数 747 :1个

那么在 4 这个格子 我们有 2 种方案,在 7 这个格子有 2 种方案,在747这个格子只有一种方案。

答案就是 C( 4, 3)*2*2*1

小技巧:求集合数是除数太大,保证精度,需要转换为逆元运算。

AC code:

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 #include <cmath>
 5 #include <map>
 6 #define INF 0x3f3f3f3f
 7 #define Mod 1000000007
 8 #define LL long long
 9 using namespace std;
10 const int MAXN = 2e5+10;
11 int aa[MAXN];
12 map<int, int>mmp;
13 int N, M;
14 LL qpow(LL x, LL n)         //求逆元
15 {
16     LL res = 1LL;
17     while(n){
18         if(n&1) res = (res*x)%Mod;
19         x = (x*x)%Mod;
20         n>>=1;
21     }
22     return res;
23 }
24 
25 LL C(LL a, LL b)    //求组合数
26 {
27     if(a < b) return 0LL; //无解
28     if(a == b) return 1LL;
29 
30     LL U = 1LL, D = 1LL;
31     for(LL i = 1; i <= a; i++) U = U*i%Mod;
32     for(LL j = 1; j <= b; j++) D = D*j%Mod;
33     for(LL j = 1; j <= a-b; j++) D = D*j%Mod;
34 //    printf("a:%lld b:%lld
", a, b);
35 //    printf("U:%lld D:%lld
", U, D);
36 
37     return U*qpow(D, (Mod-2LL))%Mod;
38 }
39 
40 bool check(int x)
41 {
42     int aa;
43     while(x){
44         aa = x%10;
45         if(aa != 4 && aa != 7) return false;
46         x/=10;
47     }
48     return true;
49 }
50 
51 int main()
52 {
53     scanf("%d %d", &N, &M);
54     int cnt = 0, num;
55     for(int i = 1; i <= N; i++){
56         scanf("%d", &num);
57         if(check(num)){
58             if(mmp[num] != 0) {cnt++;aa[cnt] = num;}
59             mmp[num]++;
60         }
61     }
62 
63     LL ans = C(N-cnt, M);
64 
65     for(int i = 1; i <= cnt; i++){
66         ans = (ans*mmp[aa[i]])%Mod;
67     }
68     printf("%lld
", ans);
69     return 0;
70 }
View Code
原文地址:https://www.cnblogs.com/ymzjj/p/10029390.html