• 组合数+dp 运


       

                                                                            问题 B: 运

    时间限制: 1 Sec  内存限制: 128 MB

    题目描述

    【问题背景】
    zhx 和妹子们玩数数游戏。

    【问题描述】

    仅包含4或7的数被称为幸运数。一个序列的子序列被定义为从序列中删去若干个数, 剩下的数组成的新序列。两个子序列被定义为不同的当且仅当其中的元素在原始序列中的下标的集合不相等。对于一个长度为 N的序列,共有 2^N个不同的子序列。( 包含一个空序列)。一个子序列被称为不幸运的, 当且仅当其中不包含两个或两个以上相同的幸运数。对于一个给定序列, 求其中长度恰好为 K 的不幸运子序列的个数, 答案 mod 10^9+7 输出。

    【输入格式】

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

    接下来一行 N 个整数 ai, 表示序列中第 i 个元素的值。

    【输出格式】

    仅一个数,表示不幸运子序列的个数。( mod 10^9+7)

    【样例输入】
    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。

            考试时读错题了,幸运数是指这个数字里只包含4或7,而两者皆有也是幸运数。10^9里幸运数不太多,可以打个表看看。统计所有出现的幸运数个数,如果某个唯一,删掉直接当普通数处理,因为无论如何也构不出两个相同的来成为幸运数。

       设幸运数种数为tot,剩下数个数为cnt

                         min(tot,k)

           ans=  Σ    C (cnt,k-i)*dp[tot][i];

                  i=0

        就是说,求k个数中放了i个不重复的幸运数的方案之和。

         dp数组的转移:dp[i][j]=dp[i-1][j]+dp[i-1][j-1]*c[i];

         i为推到第几种幸运数,j表示选了几个,c[i]即为这种幸运数的总个数。

         求组合数可以考虑用lucas

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #include<map>
    #define ll long long
    #define mod 1000000007
    #define inf 1000000000
    using namespace std;
    ll read()
    {
        ll sum=0,f=1;char x=getchar();
        while(x<'0'||x>'9'){if(x=='-')f=-1;x=getchar();}
        while(x>='0'&&x<='9'){sum=sum*10+x-'0';x=getchar();}
        return sum*f;
    }
    ll n,a[100005],k,tot,cnt,ans=0,f[2000][2000],c[2000],hh,b[2000];
    map<int,int> mp;
    int check(int x)
    {
        int p=x;
        while(x!=0)
        {
            int k=x%10;
            if(k!=4&&k!=7)return 0;
            x/=10;
        }
        mp[p]=++hh;
        return 1;
    }
    ll cheng(ll x,ll m)
    {
        ll ans=1;
        while(m)
        {
            if(m&1)ans=(ans*x)%mod;
            x=(x*x)%mod;
            m/=2;
        }
        return ans;
    }
    ll get(ll n,ll m)
    {
        if(n<m)return 0;
        if(m>n-m)m=n-m;
        ll s1=1,s2=1;
        for(int i=0;i<m;i++)
        {
            s1=s1*(n-i)%mod;
            s2=s2*(i+1)%mod;
        }
        return (s1*cheng(s2,mod-2))%mod;
    }
    int lucas(int n,int m)
    {
        if(m==0)return 1;
        return get(n%mod,m%mod)%mod*(lucas(n/mod,m/mod))%mod;
    }
    void dp()
    {
        for(int i=0;i<=tot;i++)
            f[i][0]=1;
        for(int i=1;i<=tot;i++)
            for(int j=1;j<=min(k,tot);j++)
                f[i][j]=(f[i-1][j]+f[i-1][j-1]*b[i])%mod;
    }
    int main()
    {
    //  freopen("lucky.in","r",stdin);
    //  freopen("lucky.out","w",stdout);
        n=read();k=read();
        for(int i=1;i<=n;i++)
        {
             a[i]=read();
             if(mp.count(a[i])||check(a[i]))
             {
                c[mp[a[i]]]++;
             }
             else
                cnt++;
        }
        for(int i=1;i<=hh;i++)
          if(c[i]>1)
            {
                b[++tot]=c[i];
            }
            else
              cnt++;
        dp();
        for(int i=0;i<=min(tot,k);i++)
        {
            ans=(ans+((lucas(cnt,k-i)%mod)*(f[tot][i]%mod))%mod)%mod;
        }
        cout<<ans;
    }

         

  • 相关阅读:
    几个带双下划线的宏
    WM_COPYDATA消息机制 不同进程间发送结构体数据
    解锁ubuntu系统的root
    申请堆空间函数封装(两种方法)
    UITableView的分页的加载
    UIView设置成圆角
    iPhone开发:类似iChat的聊天泡泡
    iPhone中如何自定义tabbar
    android 底部选项卡(TabHost)
    Android 多个Activity选项卡实现
  • 原文地址:https://www.cnblogs.com/QTY2001/p/7632744.html
Copyright © 2020-2023  润新知