从0,1,2...n中统计0,1,2...9各出现了多少次【SWUN1597】





题目就是说给你一个N。计算一下从0,1,2,3,4,5,,,,,,n-1,n中计算出0,1,2,3,,,,7,8,9分别出现了多少次...



#include<cstdio>
#include<cstring>

typedef unsigned __int64  LL;

LL dp[11][25][12];
/*
 *dp[k][i][j]表示记录目标数字k,第i位取j时,从0位到i位一共有多少个
 */
int bit[25];
LL tenk[22];//10^k
LL n;

inline void Init()
{
	memset(dp,-1,sizeof dp);
    int i;
    tenk[0]=1;
    for(i=1;i<22;++i)
        tenk[i]=tenk[i-1]*10;
}

inline int work(LL val)
{
    int len=0;
    for(;val;val/=10)
        bit[++len]=val%10;
	return len;
	/*
	 *把val从高位到低位存
	 *e.g.:val=132,bit=2 3 1
	 */
}

inline LL dfs(int p,int s,int tar,int u,int e)
{
	/*
	 *直接套用的数位dp模板...
	 *p: 从高位到低位位置p
	 *s: 从第一位非0数字开始s=1
	 *tar: 目标数字
	 *u: 上一步取u
	 *e: 边界
	 */
    if(p<1)
		return s&&u==tar;
    if(~dp[tar][p][u] && !e && s) return dp[tar][p][u];
    /*
     *记忆化如果目标数字tar p+1位取u,记忆过。而且s为1
     */
    int mx=e?bit[p]:9,i;
    LL ret=0;
    for(i=0;i<=mx;++i)
    {
        int news=(s||i>0);
        if(i==tar && news)
        {
        	/*
        	 *如果当前位取i而且是目标数字,而且s=1,要两种情况
        	 *如果是边界,而且不是最后一位:加剩下的
        	 *如果不是边界,而且不是最后一位: ret+=10^p-1个数
        	 *->4567 如果目标数字是4而且不是最后一位,当到第4位(从右到左,第4位取4),是边界所以ret要加4567%10^(4-1)==567
        	 *->4567 如果目标数字是4而且不是最后一位,当到第3位(从右到左,第3位取4),不是是边界所以ret要加10^(3-1)==100
        	 */
            if(e&&i==mx)
            {
                if(p!=1)
                    ret+=(n%tenk[p-1])+1;
            }else if(p!=1)
            {
                ret+=tenk[p-1];
            }
        }
        ret+=dfs(p-1,news,tar,i,e&&i==mx);
    }
    if(!e && s) dp[tar][p][u]=ret;
    return ret;
}

int main()
{
    int i;
    Init();
    while(~scanf("%I64u",&n))
    {
        int len=work(n);
        int ok=0;
        for(i=0;i<10;++i)
        {
            if(ok) putchar(32);
            ok=1;
            LL ret=dfs(len,0,i,0,1);
            if(i==0) ret++;
            printf("%I64u",ret);
        }
        putchar(10);
    }
    return 0;
}
/*
18446744073699999999

*/



原文地址:https://www.cnblogs.com/riskyer/p/3365958.html