bzoj1833: [ZJOI2010]count 数字计数
题意:
给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。
30%的数据中,a<=b<=10^6;
100%的数据中,a<=b<=10^12。
题解:
第一道数位dp题。。
统计出1-x内每种数码出现次数,由前缀和思想答案为f[b]-f[a-1]
f[x,y]表示当前考虑到第x位,数码y出现的次数,注意到数据范围应为ll
状态转移对除0外的数为f[x-1,y]*9+tp[i-1](意义即为当前为枚举1-9,后面出现y的次数+当前位为y)
对于0进行一些细节处理,即首位不能放0(但之后是可以的) #比如不要把0001这样的0统计进去
之后统计答案的时候,只需从高位到低位考虑
例如8765 只需先算1-7999 再算8000-8700。。。。。。以此类推
代码:
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll n,m,dp[30][30],tp[30],a[30],b[30],pp[30];
char st[30];
void js(ll oo[30],ll x) {
ll tmp[30];
memset(tmp,0,sizeof(tmp));
bool tt=false;
while (x!=0)
{
ll i=x,j=0;
while (i>=10) i=i/10,j++;
ll p=i;
if (tt==false)
{
for (ll i=1; i<=p-1;i++)
tmp[i]+=pp[j+1];
for (ll i=1; i<=9; i++)
tmp[i]+=p*dp[j][i];
tmp[0]+=(p-1)*dp[j][0]+tp[j];
tt=true;
} else
{
for (ll i=0; i<=p-1;i++)
tmp[i]+=pp[j+1];
for (ll i=0; i<=9; i++)
tmp[i]+=p*dp[j][i];
}
sprintf(st,"%lld",x);
ll ii=0;
for (ii=0; ii<=1ll*strlen(st)-2;ii++)
if (st[ii+1]!='0') break;
tmp[0]+=(x-p*pp[j+1]+1)*ii;
tmp[p]+=x-p*pp[j+1]+1;
x=x-p*pp[j+1];
}
memcpy(oo,tmp,sizeof(tmp));
};
int main() {
freopen("noip.in","r",stdin);
freopen("noip.out","w",stdout);
pp[1]=1;
for (ll i=1; i<=13; i++) {
pp[i+1]=pp[i]*10;
for (ll j=0; j<=9; j++)
dp[i][j]=dp[i-1][j]*10+pp[i];
tp[i]=dp[i-1][0]*9+tp[i-1];
}
tp[1]=1;
cin>>n>>m;
if (n!=1) js(a,n-1);
js(b,m);
for (ll i=0; i<=8; i++) cout<<(b[i]-a[i])<<" ";
cout<<(b[9]-a[9]);
return(0);
}