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;
}