运
【问题背景】
zhx 和妹子们玩数数游戏。
【问题描述】
仅包含 4 或7 的数被称为幸运数。
一个序列的子序列被定义为从序列中删去若干个数, 剩下的数组成的新序列。
两个子序列被定义为不同的当且仅当其中的元素在原始序列中的下标的集合不
相等。 对于一个长度为N的序列, 共有2^N个不同的子序列。包含一个空序列) 。
一个子序列被称为不幸运的,当且仅当其中不包含两个相同的幸运数。
对于一个给定序列, 求其中长度恰好为 K 的不幸运子序列的个数, 答案mod
10^9+7输出。
【输入格式】
第一行两个正整数N,K,表示原始序列的长度和题目中的 K。
接下来一行N个整数ai,表示序列中第i 个元素的值。
【输出格式】
仅一个数,表示不幸运子序列的个数。(mod
【样例输入】
3 2
1 1 1
【样例输出】
3
【样例输入】
4 2
4 7 4 7
【样例输出】
4
【样例解释】
对于样例 1,每个长度为 2的子序列都是符合条件的。
对于样例2, 4个不幸运子序列元素下标分别为: {1, 2}, {3, 4}, {1, 4}, {2, 3}。
注意下标集{1, 3}对应的子序列不是“不幸运”的,因为它包含两个相同的幸运数
4.
【数据规模与约定】
对于50%的数据,1 ≤ N≤ 16。
对于70%的数据,1 ≤ N≤ 1000, ai ≤ 10000。
对于100%的数据,1 ≤ N≤ 100000,K ≤ N, 1 ≤ ai ≤ 109。
这道题前70%的数据显然可以搜索
但是我一开始认为“仅包含4或仅包含7的数为幸运数”,然而竟过了50分
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 #define int long long 6 #define MOD 1000000007 7 #define N 1000100 8 int n,k,a[N]; 9 int ans,path[N]; 10 int ok[N][3],vis[11][2]; 11 inline int read(){ 12 int x=0; char c=getchar(); 13 while(c<'0'||c>'9') c=getchar(); 14 while('0'<=c&&c<='9') { x=(x<<3)+(x<<1)+c-'0'; c=getchar(); } 15 return x; 16 } 17 inline void check(){ 18 memset(vis,0,sizeof(vis)); 19 for(int i=1;i<=k;i++){ 20 int j=path[i]; 21 if(!ok[j][0]) continue; 22 if(vis[ok[j][2]][ok[j][1]]) return; 23 vis[ok[j][2]][ok[j][1]]=1; 24 } 25 ans++; 26 } 27 void dfs(int minn,int t){ 28 if(t==k+1){ 29 check(); 30 return; 31 } 32 for(int i=minn;i<=n;i++){ 33 path[t]=i; 34 dfs(i+1,t+1); 35 } 36 } 37 #undef int 38 int main() 39 #define int long long 40 { 41 scanf("%lld%lld",&n,&k); 42 if(n<=16){ 43 for(int i=1;i<=n;i++){ 44 a[i]=read(); 45 int k=0,b=a[i]; 46 while(b%10==4){ 47 b/=10; k++; 48 } 49 if(k&&!b) { 50 ok[i][0]=1; 51 ok[i][1]=0; ok[i][2]=k; 52 } 53 k=0,b=a[i]; 54 while(b%10==7){ 55 b/=10; k++; 56 } 57 if(k&&!b){ 58 ok[i][0]=1; 59 ok[i][1]=1; ok[i][2]=k; 60 } 61 } 62 dfs(1,1); 63 printf("%lld ",ans); 64 return 0; 65 } 66 return 0; 67 }
我们考虑仅包含4或7的数,我们可以用map
其实数本身对本题是没有影响的,
只有每种相同幸运数的个数对答案产生影响
其实我一开始是想出了正解的一半的
一开始做题时的思路:枚举k个数中不幸运数的个数i,用组合数求出;
再求出从幸运数中每种不重地选择k-i个数的方案数,相乘
然而我并不会求后者,于是直接用C(num,k-i)乘了(num为出现的幸运数的总数)
然而它可以DP
f[i][j]表示前i中幸运数选j个数的方案数
lucky[i]表示第i种幸运数的个数
f[i][j]=f[i-1][j]+f[i-1][j-1]*lucky[i]
滚动数组可以降一维空间
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<map> 5 using namespace std; 6 #define int long long 7 #define MOD 1000000007 8 #define N 100010 9 map<int,int> M; 10 int n,k,jc[N]; 11 int lucky[N],f[N],num,sum,ans; 12 inline int read(){ 13 int x=0; char c=getchar(); 14 while(c<'0'||c>'9') c=getchar(); 15 while('0'<=c&&c<='9') { x=(x<<3)+(x<<1)+c-'0'; c=getchar(); } 16 return x; 17 } 18 inline int qpow(int x,int k){ 19 int s=1; 20 while(k){ 21 if(k&1) s=s*x%MOD; 22 k>>=1; 23 x=x*x%MOD; 24 } 25 return s; 26 } 27 int C(int m,int n){ 28 if(m<n) return 0; 29 if(m==n) return 1; 30 if(n>m-n) n=m-n; 31 int x=jc[m]*qpow(jc[m-n]*jc[n]%MOD,MOD-2)%MOD; 32 return x; 33 } 34 #undef int 35 int main() 36 #define int long long 37 { 38 freopen("lucky10.in","r",stdin); 39 scanf("%lld%lld",&n,&k); 40 int x; 41 for(int i=1;i<=n;i++){ 42 x=read(); 43 int k=0,y=x; 44 while(y%10==4||y%10==7) 45 { y/=10; k++; } 46 if(k&&!y){ 47 if(!M[x]){ 48 lucky[++num]++; 49 M[x]=num; 50 } 51 else lucky[M[x]]++; 52 sum++; 53 } 54 } 55 f[0]=1; 56 for(int i=1;i<=num;i++) 57 for(int j=min(k,i);j>=1;j--) 58 f[j]=(f[j]+f[j-1]*lucky[i])%MOD; 59 jc[0]=1; 60 for(int i=1;i<=n;i++) 61 jc[i]=jc[i-1]*i%MOD; 62 for(int i=0;i<=k;i++) 63 ans=(ans+C(n-sum,i)*f[k-i]%MOD)%MOD; 64 printf("%lld ",ans); 65 return 0; 66 }