题目链接:CF258B
还是继续我的数位DP专题学习
题意:在【1,M】中选择7个数,定义数字中含有4或者7的叫做运气数,如4447有4个,可以累计
现在要求:选择第一个数的运气值要严格大于其他6个数的运气总和
分析:
第一思路:用DP预处理dp【pos】【x】:当前位为pos,运气值为x的数有多少个
但是发现这样做,有一点不好:怎么去比较严格大于这个事呢
需要这样定义:dp【pos】【x】【y】:要求最后的运气值是y!
做过这种类似的题啊!当pos==0的时候比较x和y是否相等就好
然后在注意一个前导0的问题,因为0是符合数位DP的,但是不符合题意,所以这个特殊情况要减掉
剩下的就是根据预处理的值来暴力枚举了,6层的DFS
假设我当前的运气值为x,那么剩下需要选择的6个数的运气值总和最大为x-1,带进去dfs跑就好了
上代码
#include<map>
#include<set>
#include<math.h>
#include<time.h>
#include<iostream>
#include<cstdio>
#include<queue>
#include<stack>
#include<stdio.h>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<cstdlib>
using namespace std;
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define ll rt<<1
#define rr rt<<1|1
#define LL long long
#define ULL unsigned long long
#define maxn 1050
#define maxnum 1000050
#define eps 1e-6
#define input freopen("input.txt","r",stdin)
#define output freopen("output.txt","w",stdout)
#define MOD 1000000007
LL digit[15],sum[15],dp[15][15][15];
LL dfs(int pos,int pre,int sum,bool flag){
if (pos==0) return pre==sum;
if (flag&&dp[pos][pre][sum]!=-1) return dp[pos][pre][sum];
int maxnumber=flag?9:digit[pos];
LL ans=0;
for(int i=0;i<=maxnumber;i++)
ans+=dfs(pos-1,pre+(i==4||i==7),sum,flag||i<maxnumber);
if (flag) dp[pos][pre][sum]=ans;
return ans;
}
LL solve(int now,int res){
LL ans=0;
if (now==7) return 1;
for(int i=0;i<=res;i++)
if (sum[i]){
sum[i]--;
ans=(ans+(sum[i]+1)*solve(now+1,res-i))%MOD;
sum[i]++;
}
return ans;
}
LL calc(LL n){
int len=0;
LL ans=0;
while(n){
digit[++len]=n%10;
n/=10;
}
for(int i=0;i<=len;i++)
sum[i]=dfs(len,0,i,0);
sum[0]--;
for(int i=1;i<=len;i++)
ans=(ans+sum[i]*solve(1,i-1))%MOD;
return ans;
}
int main(){
//input;
LL m;
memset(dp,-1,sizeof(dp));
while(scanf("%I64d",&m)!=EOF)
printf("%I64d\n",calc(m));
return 0;
}