http://poj.org/problem?id=2282
统计[l,r]中每个数码出现的次数
一个数位dp,之前做的方法是
dp[i][j]表示到第i位,出现了某个数码j次
对每个数码做一遍,然后再去除前导零
比较麻烦
今天发现新思路
f[i][0/1][0/1]表示 dfs到第i位之后,是否有上界限制,是否有前导零,填某个数码的数字个数
g[i][0/1][0/1][j]表示 dfs到第i位之后,是否有上界限制,是否有前导零,数码j的出现次数
#include<cstdio> #include<cstring> using namespace std; int tim,vis[10][2][2]; long long dp[10][2][2][10],f[10][2][2],ans[10]; int a[10]; void dfs(int dep,int lim,int zero) { if(vis[dep][lim][zero]==tim) return; vis[dep][lim][zero]=tim; if(!dep) { f[dep][lim][zero]=1; return; } int up=lim ? a[dep] : 9,nl,nz; for(int i=0;i<=up;++i) { nl=lim && i==a[dep]; nz=zero && !i; dfs(dep-1,nl,nz); f[dep][lim][zero]+=f[dep-1][nl][nz]; if(!nz) dp[dep][lim][zero][i]+=f[dep-1][nl][nz]; for(int j=0;j<=9;++j) dp[dep][lim][zero][j]+=dp[dep-1][nl][nz][j]; } } void solve(int n,int ty) { int len=0; while(n) a[++len]=n%10,n/=10; tim++; memset(f,0,sizeof(f)); memset(dp,0,sizeof(dp)); dfs(len,1,1); for(int i=0;i<=9;++i) ans[i]+=ty*dp[len][1][1][i]; } int main() { int l,r; while(1) { scanf("%d%d",&l,&r); if(!l && !r) return 0; if(l>r) l^=r,r^=l,l^=r; memset(ans,0,sizeof(ans)); solve(r,1); solve(l-1,-1); for(int i=0;i<=9;++i) printf("%lld%c",ans[i],i==9 ? ' ' : ' '); } }