[bzoj 1833] [ZJOI2010]count 数字计数:数位DP
2016-11-07 16:02
555 查看
题意:求[a, b]之间的所有整数中各个数码出现了多少次。(1<=a<=b<=10^12,a, b是整数)
考虑[1, x)内各个数码出现了多少次。设
[1, x)内的数可分为三类:
1. 位数少于x。
2. 最高位小于x。
3. 和x有公共的前缀。
分类统计,第二、三类可以合并。设现在考虑从高往低第k位,则
看了Po姐的题解,感觉比我写的要美……
位数少于x的数,枚举1~9作为最高位。最高位中每个数码出现
和x有公共前缀的数,自由的非公共部分每个数码出现
于是又写了一个版本。
考虑[1, x)内各个数码出现了多少次。设
f[i][j][k]为以j开头的(i+1)位十进制串中k出现的次数,递推。
[1, x)内的数可分为三类:
1. 位数少于x。
2. 最高位小于x。
3. 和x有公共的前缀。
分类统计,第二、三类可以合并。设现在考虑从高往低第k位,则
f数组包含k及其右边的数码,k左边的数码还需要单独计算。
看了Po姐的题解,感觉比我写的要美……
f[i][j][k]可简化为
f[i](依然允许前导0),因为每个数码出现的次数相同——只是一些字符串罢了。有递推式
f[i] = f[i-1]*10 + 10^i,其中
f[i-1]*10是低i位中某数码的出现次数(用0~9共10个数码作为最高位),
10^i是它在最高位的出现次数。
位数少于x的数,枚举1~9作为最高位。最高位中每个数码出现
10^i次,低i位中每个数码出现
f[i-1]*9次。
和x有公共前缀的数,自由的非公共部分每个数码出现
f[i-1]次,公共部分和非自由非公共部分一起暴力统计。公共部分可以每次计算,也可以像我的代码这样放在一起考虑,如x=1234,则1在公共部分出现234次,2在公共部分出现34次,3在公共部分出现4次。
于是又写了一个版本。
#include <cstdio> #include <cstring> using namespace std; const int MAX_N = 13; typedef long long ll; // f[i][j][k]: 以j打头的(i+1)位十进制串含多少个k // g[i][j] = Sigma(f[i][*][j]) ll p[MAX_N], f[MAX_N][10][10], g[MAX_N][10], v[2][10]; void cal(ll x, ll a[10]) { ll b[MAX_N]; int n = 0; for (ll y = x; y; y /= 10) b[n++] = y % 10; for (int i = 0; i < n-1; ++i) for (int j = 0; j < 10; ++j) a[j] += g[i][j] - f[i][0][j]; for (int i = n-1; i >= 0; --i) for (int j = (i == n-1); j < b[i]; ++j) for (int k = 0; k < 10; ++k) a[k] += f[i][j][k]; for (ll i = 1, y = b[0]; i < n; y = y + b[i]*p[i], ++i) a[b[i]] += y; } int main() { ll a, b; scanf("%lld %lld", &a, &b); for (int i = 0; i < 10; ++i) f[0][i][i] = g[0][i] = 1; p[0] = 1; for (int i = 1; i < MAX_N; ++i) { p[i] = p[i-1]*10; for (int j = 0; j < 10; ++j) { f[i][j][j] = p[i]; for (int k = 0; k < 10; ++k) { f[i][j][k] += g[i-1][k]; g[i][k] += f[i][j][k]; } } } cal(b+1, v[0]); cal(a, v[1]); for (int i = 0; i < 10; ++i) printf("%lld%c", v[0][i]-v[1][i], " \n"[i == 9]); return 0; }
#include <cstdio> using namespace std; typedef long long ll; const int MAX_N = 13; ll f[MAX_N], p[MAX_N], v[2][10]; void cal(ll x, ll a[10]) { ll b[MAX_N]; int n = 0; for (ll y = x; y; y /= 10) b[n++] = y % 10; for (int i = 0; i < n-1; ++i) for (int j = 0; j < 10; ++j) a[j] += (j ? p[i] : 0) + (i ? f[i-1]*9 : 0); // 最高位,低i位 for (int i = n-1, c; c = 0, i >= 0; --i) { for (int j = (i==n-1); j < b[i]; ++c, ++j) a[j] += p[i]; for (int j = 0; j < 10; ++j) a[j] += i ? c*f[i-1] : 0; } for (ll i = 1, y = b[0]; i < n; y += p[i]*b[i], ++i) a[b[i]] += y; } int main() { ll a, b; scanf("%lld %lld", &a, &b); p[0] = f[0] = 1; for (int i = 1; i < MAX_N; ++i) { p[i] = p[i-1]*10; f[i] = f[i-1]*10 + p[i]; } cal(b+1, v[0]); cal(a, v[1]); for (int i = 0; i < 10; ++i) printf("%lld%c", v[0][i] - v[1][i], " \n"[i == 9]); return 0; }
相关文章推荐
- BZOJ1833 [ZJOI2010]count 数字计数 【数学 Or 数位dp】
- BZOJ_1833_[ZJOI2010]count 数字计数_数位DP
- 【bzoj 1833】【codevs 1359】 [ZJOI2010]count 数字计数(数位dp)
- 【BZOJ1833】【ZJOI2010】count 数字计数 (数位DP)
- bzoj 1833 [ZJOI2010]count 数字计数(数位DP)
- bzoj1833: [ZJOI2010]count 数字计数(数位dp)
- 【BZOJ】1833: [ZJOI2010]count 数字计数(数位dp)
- 【bzoj1833】[ZJOI2010]count 数字计数 数位DP
- [BZOJ1833] [ZJOI2010]count 数字计数 && 数位DP
- bzoj 1833: [ZJOI2010]count 数字计数【数位dp】
- bzoj 1833: [ZJOI2010]count 数字计数(数位dp)
- [省选前题目整理][BZOJ 1833][ZJOI 2010]count 数字计数(数位DP)
- BZOJ 1833: [ZJOI2010]count 数字计数 数位DP,处理前导0
- BZOJ 1833 [ZJOI2010]count 数字计数(数位dp)
- [BZOJ 1833][ZJOI 2010]count数字计数(数位DP)
- [BZOJ 1833] [ZJOI2010] count 数字计数 【数位DP】
- [BZOJ1833][ZJOI2010]count 数字计数(数位dp)
- BZOJ 1833: [ZJOI2010]count 数字计数 【数位DP】
- 【BZOJ 1833】 [ZJOI2010]count 数字计数|数位DP
- [bzoj1833][ZJOI2010]count 数字计数——数位dp