Dream Counting, 2006 Dec-数数的梦数位dp
Posted Konjak谷弱
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 }
以上是关于Dream Counting, 2006 Dec-数数的梦数位dp的主要内容,如果未能解决你的问题,请参考以下文章
bzoj4397[Usaco2015 dec]Breed Counting*
P4818 [USACO15DEC]Bessie's Dream 贝西的梦
[bzoj4397] [Usaco2015 dec]Breed Counting