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

从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 */
上一篇:NetSuite Chinese Finance Reports


下一篇:BZOJ1131[POI2008]Sta——树形DP