数位dp + dfs + 逆元求余 CF 258 B. Little Elephant and Elections

题目链接:

http://codeforces.com/problemset/problem/258/B

题目意思:

给你一个m(m<=1^9),把1-m这m个数拿出7个分给7个政党,要求其中一个政党分配的数中含4和7的总个数比其他6个政党含4和7的总数还要多,求分配的总的种数,对1000000007求余。

解题思路:

先用数位dp,求出含有0个4或7的数的个数,含有1个4或7的数的个数,含。。。。。

dp[cur][last][sum]:表示还有cur位,前面一共含有last个4或者7,总共要求含有sum个4或者7的总的个数

然后分配的时候用递归来实现,其中组合数求余用到了求逆元的方法。

代码:

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<stack>
#include<list>
#include<queue>
#define eps 1e-6
#define INF (1<<30)
#define PI acos(-1.0)
using namespace std;

#define mod 1000000007
#define ll __int64
int num[15];
int dp[15][15][15],pos[15];

int dfs1(int cur,int last,int sum,int flag)
{
   if(!cur)
   {
      if(last==sum)
         return 1;
      return 0;
   }
   if(last>sum)
      return 0;

   if(!flag&&dp[cur][last][sum]!=-1)
   {
      return dp[cur][last][sum];
   }

   int Max=flag?pos[cur]:9,numm=0;

   for(int i=0;i<=Max;i++)
   {
      if(i==7||i==4)
         numm+=dfs1(cur-1,last+1,sum,flag&&i==Max);
      else
         numm+=dfs1(cur-1,last,sum,flag&&i==Max);
   }
   if(!flag)
      dp[cur][last][sum]=numm;
   return numm;
}

void Cal(int m)
{
   int tt=0;

   while(m)
   {
      ++tt;
      pos[tt]=m%10;
      m/=10;
   }
   for(int i=0;i<=9;i++) //表示含有i个4或者7的总数
      num[i]=dfs1(tt,0,i,1);
}

ll AA(int n,int m)
{
   ll res=1;

   for(int i=n;i>=n-m+1;i--)
      res=(res*i)%mod;
   return res;
}

ll quick(ll m,ll n)  //快速幂求m^n%mod
{
   ll res=1;

   while(n)
   {
      if(n&1)
         res=(res*m)%mod;
      m=(m*m)%mod;
      n=n>>1;
   }
   return res;

}

ll CC(int n,int m)  //求组合数C(n,m)
{
   ll res=AA(n,m);

   ll temp=1;

   for(int i=2;i<=m;i++)
      temp=(temp*i)%mod;

   return (res*quick(temp,mod-2))%mod;

}

ll dfs2(int start,int sum,int left) //搜索求总的排列数
{
   if(left==0) //选出了6个政党
      return AA(6,6); //A66 采用先选后排的方法

   if(start<0) //不够
      return 0;

   ll res=0;
   for(int i=0;i<=num[start]&&(start*i)<sum&&i<=left;i++)
      res=(res+(CC(num[start],i)*dfs2(start-1,sum-start*i,left-i))%mod)%mod;
   return res;
}

int main()
{

   int m;
   memset(dp,-1,sizeof(dp));

   while(scanf("%d",&m)!=EOF)
   {
      memset(num,0,sizeof(num));
      Cal(m);
      num[0]--; //题目要求从1-m
      ll ans=0;
      for(int i=9;i>=1;i--)
         if(num[i])
            ans=(ans+(num[i]*dfs2(i-1,i,6))%mod)%mod;
      printf("%I64d\n",ans);
   }

   return 0;
}




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值