您的位置:首页 > 其它

BZOJ1833 [ZJOI2010]count 数字计数 【数学 Or 数位dp】

2018-04-07 17:08 549 查看

题目

给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码(digit)各出现了多少次。

输入格式

输入文件中仅包含一行两个整数a、b,含义如上所述。

输出格式

输出文件中包含一行10个整数,分别表示0-9在[a,b]中出现了多少次。

输入样例

1 99

输出样例

9 20 20 20 20 20 20 20 20 20

提示

30%的数据中,a<=b<=10^6;
100%的数据中,a<=b<=10^12。

题解

你以为我真的会写数位dp?

首先容斥一下,转化为求小于等于n的方案数

如果不考虑前缀0,那么就只需要递归处理不大于n所有数字出现的次数
考虑前缀0,我们再减去开头有若干个0的方案数

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
using namespace std;
const int maxn = 15,maxm = 100005,INF = 1000000000;
LL g[maxn],Pow[maxn];
void init(){
Pow[0] = 1;
for (int i = 1; i < maxn; i++) Pow[i] = Pow[i - 1] * 10;
g[1] = 1;
for (int i = 2; i < maxn; i++){
g[i] = 10 * g[i - 1] + Pow[i - 1];
}
}
struct node{
LL t[10];
node(){memset(t,0,sizeof(t));}
};
node cal(LL n,LL h,LL tmp){
//cout << n << endl;
node re,t;
if (h == 1){
for (int i = 0; i <= n; i++) re.t[i] = 1;
return re;
}
for (int i = 0; i <= 9; i++){
re.t[i] += (n / tmp) * g[h - 1];
if (i < n / tmp) re.t[i] += Pow[h - 1];
}
re.t[n / tmp] += n - (n / tmp) * tmp + 1;
t = cal(n % tmp,h - 1,tmp / 10);
for (int i = 0; i <= 9; i++) re.t[i] += t.t[i];
return re;
}
node solve(LL n){
LL h = 1,tmp = 1;
for (LL i = n; i / 10; i /= 10) h++,tmp *= 10;
node re = cal(n,h,tmp);
for (int i = 1; i < h; i++){
re.t[0] -= Pow[h - i];
}
return re;
}
int main(){
init();
LL a,b;
cin >> a >> b;
node ansr = solve(b),ansl = solve(a - 1);
for (int i = 0; i < 9; i++) printf("%lld ",ansr.t[i] - ansl.t[i]);
printf("%lld",ansr.t[9] - ansl.t[9]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: