P2602 [ZJOI2010]数字计数(数位dp)
题意简单明了:
给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。
显然要使用数位dp,数码的出现次数很显然满足前缀和的性质,所以只需要分别计算出\(a-1\)和\(b\)的答案即可
这里也要使用经典套路,先把十进制数字离散化存在一个数组里,然后由高位向低位搜索
记忆化搜索里传进去五个参数:
**
digit:当前要求解的数码
len:数字的剩余长度
sum:目前求出的当前数码出现的次数
zero:是否含有前导0
small:前面有没有某一位小于上限
**
其他参见代码,一顿转移即可:
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
const int N=25;
ll a,b;
int num[N];
int dp[N][N][2][2];
ll gao2(int digit,int len,ll sum,bool zero,bool small)
{
ll ret=0;
if(len==0) return sum;//len为0的时候不需要记忆,之后不会再次用到len为0的状态
if(dp[len][sum][zero][small]!=-1) return dp[len][sum][zero][small];
for(int i=0;i<10;++i)
{
if(!small&&i>num[len]) break;
ret+=gao2(digit,len-1,sum+((!zero||i)&&i==digit),zero&&i==0,small||(i<num[len]));
}
dp[len][sum][zero][small]=ret;
return ret;
}
ll gao(ll x,int digit)
{
int len=0;
while(x)
{
num[++len]=x%10;
x/=10;
}
memset(dp,-1,sizeof(dp));
return gao2(digit,len,0,1,0);
}
signed main()
{
scanf("%lld%lld",&a,&b);
for(int i=0;i<10;++i)
printf("%lld ",gao(b,i)-gao(a-1,i));
return 0;
}
注意的是不知道哪儿爆了int,开了define int long long才过