1. 程式人生 > >[ZJOI 2010]count 數字計數

[ZJOI 2010]count 數字計數

整數 HR work pos 區間 using 證明 script 情況

Description

題庫鏈接

問你 \([l,r]\) 區間內所有整數中各個數碼出現了多少次。

\(1\leq a\leq b\leq 10^{12}\)

Solution

數位 \(DP\)

定義一個函數 \(cal(i)\) 為求 \([1,i)\) 中所有整數各個數碼的和。顯然答案就是 \(cal(r+1)-cal(l)\)

考慮如何求 \(cal(x)\)

先考慮數的位數小於 \(x\) 的情況。假設 \(x\) 的總位數為 \(tot\) 。我們可以從 \(1\sim tol-1\) 來枚舉最高位。對於每次枚舉的最高位 \(i\) ,顯然最高位上的每個數都會計算 \(10^{i-1}\)

次, \(0\) 除外。再考慮以其為最高位開頭。之後的所有數碼產生的新貢獻為 \(9(i-1)\cdot 10^{i-2}\) 其中 \(9\) 表示最高位有 \([1,9]\)\(9\) 個數; \(i-1\) 表示之後共有 \(i-1\) 位。 \(10^{i-2}\) 指的是每一位會產生貢獻(推推式子可以證明後面的每一位的數碼出現頻率是相同的)。

最高位等於 \(tot\) 的情況和上面是類似的,假設 \(x_i\)\(x\) 的第 \(i\) 位上的數字。顯然對於小於 \(x_i\) 的所有數碼(包括除最高位外的 \(0\) ),都會計算 \(10^{i-1}\) 次。而 \(x_i\)

只會計算 \(last\) 次,其中 \(last\)\(i\) 位之後的數字;之後的所有數碼產生的新貢獻和上面類似,詳可見代碼。

Code

//It is made by Awson on 2018.2.28
#include <bits/stdc++.h>
#define LL long long
#define dob complex<double>
#define Abs(a) ((a) < 0 ? (-(a)) : (a))
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
#define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b)) #define writeln(x) (write(x), putchar('\n')) #define lowbit(x) ((x)&(-(x))) using namespace std; void read(LL &x) { char ch; bool flag = 0; for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == '-')) || 1); ch = getchar()); for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar()); x *= 1-2*flag; } void print(LL x) {if (x > 9) print(x/10); putchar(x%10+48); } void write(LL x) {if (x < 0) putchar('-'); print(Abs(x)); } LL x, y, cnt[15]; void cal(LL x) { LL a[20], tot = 0, mi = 1, last = 0; while (x) a[++tot] = x%10, x /= 10; for (int i = 1; i < tot; i++) { for (int d = 1; d < 10; d++) cnt[d] += mi; for (int d = 0; d < 10; d++) cnt[d] += mi*9/10*(i-1); mi *= 10; } mi = 1; for (int i = 1; i <= tot; i++) { cnt[a[i]] += last; int begin = (i == tot); for (int d = begin; d < a[i]; d++) cnt[d] += mi; if (a[i]) for (int d = 0; d < 10; d++) cnt[d] += mi*(a[i]-begin)/10*(i-1); last += mi*a[i], mi *= 10; } } void work() { read(x), read(y); cal(x); for (int i = 0; i < 10; i++) cnt[i] = -cnt[i]; cal(y+1); for (int i = 0; i < 9; i++) write(cnt[i]), putchar(' '); writeln(cnt[9]); } int main() { work(); return 0; }

[ZJOI 2010]count 數字計數