题意:给定两个数,问区间[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 }
时间: 2024-10-29 07:38:10