P2602 [ZJOI2010]数字计数

原题链接
考察:数位dp
思路:
  预处理f[i][j][k]表示前i位数,最高位是j,k出现的次数.
 f[i][j][k] = \(\sum_{t=0}^9\) f[i-1][t][k]; (j!=k)
 f[i][j][k] = 10i-1+\(\sum_{t=0}^9\) f[i-1][t][k]; (j!=k,i-1位一共10i-1个数)
  接下来按模板写即可,但是注意当数位上的数刚好==k,那么res+=下一位数x*sum(出现次数)*下一位的位数(表示[0,x)之间需要计数)

Code

#include <iostream> 
#include <cstring>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 20;
LL a,b;
LL f[N][N][N],ans[N],logs[N];
void init()
{//当位数为i,最高位为j时,出现 k的次数 
    logs[0] = 1;
	for(int i=0;i<10;i++) f[1][i][i] = 1;
	for(int i=1;i<N;i++) logs[i] = logs[i-1]*10;
	for(int i=2;i<N;i++)
	 for(int k=0;k<10;k++)
	   for(int j=0;j<10;j++)
	   {
	   	 if(j==k) f[i][j][k]+=logs[i-1];
	   	 for(int t=0;t<10;t++)
	   	   f[i][j][k]+=f[i-1][t][k];
	   }
}
LL dp(LL n,int a)
{
	if(!n) return n==a;
	vector<int> v;
	while(n) v.push_back(n%10),n/=10;
	LL res = 0; int sum = 0;
	if(a)
	{
		for(int i=v.size()-1;i>=0;i--)
		{
			int x = v[i];
			for(int j=0;j<x;j++)
		 		res+=f[i+1][j][a];
		 	res+=(LL)sum*x*logs[i];//sum对后面的[0,x*logs[i])的贡献 
		 	if(a==x) sum++;
		 	if(!i) res+=sum;
		}
	}else{
		for(int i=v.size()-2;i>=0;i--)
		{
			int x = v[i];
			for(int j=0;j<x;j++)
			  res+=f[i+1][j][a];
			res+=(LL)sum*x*logs[i];//sum对后面的[0,x)的贡献 
		 	if(a==x) sum++;
		 	if(!i) res+=sum;
		}
		for(int i=1;i<v.back();i++)
		  res+=f[v.size()][i][a];
		res++;
		for(int i=1;i<v.size();i++)//长度 
		  for(int j=1;j<10;j++)
		   res+=f[i][j][a];
	}
	return res;
}
int main()
{
	init();
	while(scanf("%lld%lld",&a,&b)!=EOF&&(a+b))
	{
		if(a>b) swap(a,b);
		for(int i=0;i<=9;i++)
		  ans[i] = dp(b,i)-dp(a-1,i);
		for(int i=0;i<=9;i++)
		  printf("%lld ",ans[i]);
		printf("\n");
	}
	return 0;
}
posted @ 2021-05-31 02:06  acmloser  阅读(35)  评论(0编辑  收藏  举报