题目描述
简洁意思为给定一个区间,求区间中0-9出现的次数。
数据范围
0<a,b<100000000
算法题解
此题由提高课模板改编orz, f(i,j,k)表示一共有i位,且最高位为j的数中,k出现的次数。
f(i,j,k)可由f(i-1,0-9,k)转化而来,如果j=k,那f(i,j,k)就要加上10的i-1次方,例如
求f(2,2,2),由f(1,x,2)转化时候实则开头为2的数的首位2并没有被计算。由此预处理完成
接下来是dp每一位的过程,由于前导零不合法,我们需要特判,而特判比较麻烦,因此判断
如果是首位就从1开始枚举,反之从0开始,最后再暴力枚举1到n-1位数(n为数的总位数)
换句话说,枚举1的时候等价于存在n-1个前导零。另外,dp过程中需要注意的是当此位数
等于我们需要求的数的个数的时候,需要遍历后面位数,遍历原理等同于预处理没有被计算的
部分。细节很多,题主在细节上debug了好久orz 果然还是太菜了。深夜记录写下此文记录一下
辛酸过程。
C++ 代码
#include <iostream>
#include <vector>
using namespace std;
const int N=11;
long long f[N][N][N];
void init()
{
for(int i=0;i<=9;i++)
{
f[1][i][i]=1;
}
for(int i=2;i<N;i++)
{
long long u=1;
for(int w=1;w<i;w++)u*=10;
for(int j=0;j<=9;j++)
{
for(int k=0;k<=9;k++)
{
for(int z=0;z<=9;z++)
{
f[i][j][k]+=f[i-1][z][k];
}
if(j==k)f[i][j][j]+=u;
}
}
}
}
int dp(int n,int r)
{
if(!n)return 0;
int t=n;
vector <int> nums;
while(n)nums.push_back(n%10),n/=10;
int res=0,last=-1;
for(int i=nums.size()-1;i>=0;i--)
{
int x=nums[i];
for(int j=i==nums.size()-1;j<x;j++)
{
res+=f[i+1][j][r];
}
if(!i)
{
while(t)
{
if((t%10)==r)res++;
t/=10;
}
}
if(x==r)
{
long long p=0;
for(int z=i-1;z>=0;z--)
{
p=p*10+nums[z];
}
res+=p;
}
}
for(int i=1;i<nums.size();i++)
{
for(int j=1;j<=9;j++)
{
res+=f[i][j][r];
}
}
return res;
}
int main(void)
{
init();
int l,r;
while(cin>>l>>r,l||r)
{
if(l>r)swap(l,r);
for(int i=0;i<=9;i++)
{
cout<<dp(r,i)-dp(l-1,i)<<" ";
}
cout<<endl;
}
return 0;
}
太强了啊啊啊啊
可以替换
orz,终于搞懂了,看到这篇题解真的是”wa的一声哭出来 “
谢谢支持~~~~
厉害