向前走莫回头❤

【bzoj 1833】【codevs 1359】 [ZJOI2010]count 数字计数(数位dp)

1833: [ZJOI2010]count 数字计数

Time Limit: 3 Sec  Memory Limit: 64 MB
Submit: 2774  Solved: 1230
[Submit][Status][Discuss]

Description

给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。

Input

输入文件中仅包含一行两个整数a、b,含义如上所述。

Output

输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次。

Sample Input

1 99

Sample Output

9 20 20 20 20 20 20 20 20 20

HINT

30%的数据中,a<=b<=10^6;
100%的数据中,a<=b<=10^12。

Source

[Submit][Status][Discuss]

【题解】【数位dp】

[自己乱搞的结果就是写了三遍。。。每一遍想法都有所改变……蠢哭]

【刚开始想的是用f[i][j][k][o][x][y]表示第i位放j时k的出现次数,o表示前导0,x,y表示是否到达左右边界,每次下一位与当前位的相同时,更新时就加1,然后,怎么搞都错成一团。。。扔掉!】

【然后,又考虑用f[i][j][k][o][x][y]表示第i位放j时j出现的次数k,o表示前导0,x,y表示是否到达左右边界,每一次都是加上更新过来的f[i][j][k][o][x][y],然后,发现怎么搞1都不对。。。又扔掉!】

【最后,结合前两次的经验,先循环一重当前要求数t的出现次数,用f[i][j][k][o][x][y]表示第i位放j时t的出现次数k,o表示前导0,x,y表示是否到达左右边界。循环9次,卡过了。。。】

【注意,这种方法,由于要判断前导0,把零先去掉了,如果区间中包含0的话,要特判】

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
ll a,b,ans;
int al[15],ar[15],left[15],right[15],l,r,len;
int f[15][12][15][3][3][3];
int main()
{
	freopen("int.txt","r",stdin);
	freopen("my.txt","w",stdout);
	int i,j,k,xx;
	scanf("%lld%lld",&a,&b); xx=a;
	while(a) left[++l]=a%10,a/=10;
	while(b) right[++r]=b%10,b/=10;
	len=max(l,r);
	memset(f,0,sizeof(f));
	for(i=1;i<=len;++i) al[len-i+1]=left[i],ar[len-i+1]=right[i];
	for(int t=0;t<=9;++t)
	 {
	 	memset(f,0,sizeof(f));
		for(i=al[1];i<=ar[1];++i) f[1][i][i==t&&i!=0][i==0][i==al[1]][i==ar[1]]=1;
	 	for(i=1;i<len;++i)
	 	 for(j=0;j<=9;++j)
	 	  for(k=0;k<=12;++k)
	 	   for(int o=0;o<=1;++o)
	 	    for(int x=0;x<=1;++x)
	 	     for(int y=0;y<=1;++y)
	 	      if(f[i][j][k][o][x][y])
	 	       {
	 	       	    if(x&&y)
	 	       	     {
	 	       	 	    for(int h=al[i+1];h<=ar[i+1];++h)
	 	       	 	     {
	 	       	 	     	int p;
	 	       	 	 	    if(h||!o) p=k+(t==h),f[i+1][h][p][o&(h==0)][x&(h==al[i+1])][y&(h==ar[i+1])]+=f[i][j][k][o][x][y];
	 	       	 	 	      else p=0,f[i+1][h][0][o&(h==0)][x&(h==al[i+1])][y&(h==ar[i+1])]+=f[i][j][k][o][x][y];
					    }
					    continue;	
				     } 	
				    if(!x&&!y)
				     {
				     	for(int h=0;h<=9;++h)
				     	 {
	 	       	 	     	int p;
	 	       	 	 	    if(h||!o) p=k+(t==h),f[i+1][h][p][o&(h==0)][x&(h==al[i+1])][y&(h==ar[i+1])]+=f[i][j][k][o][x][y];
	 	       	 	 	      else p=0,f[i+1][h][p][o&(h==0)][x&(h==al[i+1])][y&(h==ar[i+1])]+=f[i][j][k][o][x][y];
	 	       	 	    }
					    continue;	
					 }
					if(!x)
					 {
					 	for(int h=0;h<=ar[i+1];++h)
					 	 {
	 	       	 	     	int p;
	 	       	 	 	    if(h||!o) p=k+(t==h),f[i+1][h][p][o&(h==0)][x&(h==al[i+1])][y&(h==ar[i+1])]+=f[i][j][k][o][x][y];
	 	       	 	 	      else p=0,f[i+1][h][p][o&(h==0)][x&(h==al[i+1])][y&(h==ar[i+1])]+=f[i][j][k][o][x][y];
					    }
					    continue;	
					 }
					if(!y)
					 {
					 	for(int h=al[i+1];h<=9;++h)
					 	 {
	 	       	 	     	int p;
	 	       	 	 	    if(h||!o) p=k+(t==h),f[i+1][h][p][o&(h==0)][x&(h==al[i+1])][y&(h==ar[i+1])]+=f[i][j][k][o][x][y];
	 	       	 	 	      else p=0,f[i+1][h][p][o&(h==0)][x&(h==al[i+1])][y&(h==ar[i+1])]+=f[i][j][k][o][x][y];
					    }
					    continue;	
					 }
				}
		ans=0;
		for(i=0;i<=9;++i)
		 for(j=1;j<=12;++j)
		  for(k=0;k<=1;++k)
		   ans+=(ll)(f[len][i][j][k][1][1]+f[len][i][j][k][1][0]+f[len][i][j][k][0][1]+f[len][i][j][k][0][0])*j;
		if(!t&&!xx) printf("%lld ",ans+1);
		 else printf("%lld ",ans);
		if(t==9) printf("\n");
	 }
	return 0;
}


posted @ 2016-09-24 21:05  lris0-0  阅读(71)  评论(0编辑  收藏  举报
过去的终会化为美满的财富~o( =∩ω∩= )m