曦皓的幸运数

【题目描述】

曦皓认为,幸运数是那些仅包含4或7的数。

规定一个序列的子序列为从序列中删去若干个数,剩下的数组成的新序列,两个子序列为不同的当且仅当其中的元素在原始序列中的下标的集合不相等,对于一个长度为N的序列,共有2N个不同的子序列(包含1个空序列),一个子序列为不幸运的当且仅当其中不包含两个相同的幸运数。

现给定一个序列,询问其长度恰好为K的不幸运子序列的个数。

【输入描述】

第一行输入两个正整数N、K,表示原始序列的长度和题目中的K;

第二行输入N个整数ai,表示序列中第i个元素的值。

【输出描述】

输出一个数,答案 mod (109+7)的值。

【样例输入】

样例1:

3 2

1 1 1

 

样例2:

4 2

4 7 4 7

【样例输出】

样例1:

3

 

样例2:

4

【数据范围及提示】

对于样例1,每个长度为2的子序列都是符合条件的;

对于样例2,4个不幸运子序列元素下标分别为:{1,2}、{3,4}、{1,4}、{2,3},注意下标集{1,3}对应的子序列不是“不幸运”的,因为它包含两个相同的幸运数4;

源代码:

#include<cstdio>
#include<algorithm>
#define LL long long
#define INF 1000000007
using namespace std;
LL M,N,K,X,Y,Ans(0),Num(0),Luck[2100],Sum[2100],C[100001],f[2100];
void DFS(LL S,LL T) //预处理幸运数。
{
    if (T)
      Luck[++Num]=T;
    if (S==10) //按照数据范围定义边界。
      return;
    DFS(S+1,T*10+4);
    DFS(S+1,T*10+7);
}
void ExGCD(LL t1,LL t2) //扩展欧几里得。
{
    if (!t2)
    {
        X=1;
        Y=0;
    }
    else
    {
        ExGCD(t2,t1%t2);
        LL t=X;
        X=Y;
        Y=t-(t1/t2)*Y;
    }
}
LL Count(LL T) //逆元。
{
    ExGCD(T,INF);
    return (X%INF+INF)%INF;
}
void Work() //处理不幸运的数。
{
    C[0]=1;
    for (LL a=0;a<M;a++) //排列组合。
      C[a+1]=C[a]*(M-a)%INF*Count(a+1)%INF;
}
void Solve() //动态规划。
{
    f[0]=1;
    for (LL a=1;a<=Num;a++)
      if (Sum[a]>=2)
      {
        for (LL b=Num;b;b--)
        f[b]=(f[b-1]*Sum[a]%INF+f[b])%INF;
      }
}
int main()
{
    scanf("%I64d%I64d",&N,&K);
    DFS(1,0);
    sort(Luck+1,Luck+Num+1);
    M=N;
    for (LL a=1;a<=N;a++)
    {
        LL t;
        scanf("%I64d",&t);
        LL T=lower_bound(Luck+1,Luck+Num+1,t)-Luck; //二分查找STL(大于等于)。
        if (Luck[T]==t)
        {
            Sum[T]++; //此幸运数个数。
            if (Sum[T]==2)
              M-=2;
            if (Sum[T]>2)
              M--;
        }
    }
    Work();
    Solve();
    for (LL a=0;a<=K;a++)
      Ans=(C[a]*f[K-a]%INF+Ans)%INF;
    printf("%I64d",Ans);
    return 0;
}

/*
    挺有意思的一道题。
    可以发现,数列由幸运数和非幸运数组成。
    非幸运数的选择方案数为C(非幸运数总数,选多少非幸运数)。
    幸运数的选择方案就需要DP了,因为它只能选一个!
    设f[i][j]表示前i个幸运数取j个,则状态转移方程为:
        f[i][j]=f[i-1][j]+f[i-1][j-1]*Sum[i]
    最后取和即可。
*/
原文地址:https://www.cnblogs.com/Ackermann/p/5859686.html