1. 程式人生 > >“東信杯”廣西大學第一屆程式設計競賽(同步賽) B 不吉利的數 【規律 逆元 排列組合】

“東信杯”廣西大學第一屆程式設計競賽(同步賽) 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\n", a, b);
35 //    printf("U:%lld D:%lld\n", 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\n", ans);
69     return 0;
70 }
View Code