【Dream Counting, 2006 Dec-数数的梦】数位dp

题意:给定两个数,问区间[A,B]中0~9分别出现了多少次。A,B<=10^18

题解:应该是最裸的数位dp吧。。一开始没有记忆化tle了TAT

我们可以求出区间[0,B]的,再减去区间[0,A]的。

用dfs实现,记录flag(填了的位是否和边界重合),zero(当前是否还在前缀0中)

 1 #include<cstdio>
 2 #include<cstdlib>
 3 #include<cstring>
 4 #include<iostream>
 5 #include<ctime>
 6 #include<queue>
 7 using namespace std;
 8 
 9 typedef long long LL;
10 const int N=100;
11 int c[N][N][2][2],d[N],sum[N];
12 LL bit[N],ret[N];
13 
14 LL g(int now,int x,int flag,int zero)
15 {
16     if(c[now][x][flag][zero]!=-1) return c[now][x][flag][zero];
17     if(x==0) return 0;
18     LL ans=0;
19     if(flag)
20     {
21         for(int i=0;i<d[x];i++) 
22         {
23             ans+=g(now,x-1,0,zero & (i==0));
24             if(i==now && !(i==0 && zero)) ans+=bit[x-1];
25         }
26         ans+=g(now,x-1,1,zero && (d[x]==0));
27         if(now==d[x] && !(now==0 && zero)) ans+=ret[x-1]; 
28     }
29     else
30     {
31         for(int i=0;i<=9;i++) 
32         {
33             ans+=g(now,x-1,0,zero & (i==0)); 
34             if(i==now && !(i==0 && zero)) ans+=bit[x-1];
35         }
36     }
37     // printf("now %d  x %d  flag %d  zero %d  = %d\n",now,x,flag,zero,ans);
38     c[now][x][flag][zero]=ans;
39     return ans;
40 }
41 
42 void solve(LL x,LL tmp)
43 {
44     memset(d,0,sizeof(d));
45     memset(c,-1,sizeof(c));
46     int l=0;LL y=x;
47     while(y)
48     {
49         d[++l]=(int)(y%10);
50         y/=10;
51     }
52     for(int i=0;i<=18;i++) ret[i]=x%bit[i]+1;
53     for(int i=0;i<=9;i++) sum[i]+=g(i,20,1,1)*tmp;
54 }
55 
56 int main()
57 {
58     // freopen("a.in","r",stdin);
59     freopen("dream.in","r",stdin);
60     freopen("dream.out","w",stdout);
61     LL A,B;int x;
62     scanf("%lld%lld",&A,&B);
63     memset(sum,0,sizeof(sum));
64     bit[0]=1;
65     for(int i=1;i<=18;i++) bit[i]=bit[i-1]*10;
66     solve(A-1,-1);
67     solve(B,1);
68     for(int i=0;i<=9;i++) printf("%d ",sum[i]);
69     return 0;
70 }

 

posted @ 2016-11-02 09:28  拦路雨偏似雪花  阅读(271)  评论(0编辑  收藏  举报