SP10606 BALNUM - Balanced Numbers 题解
只能说,数位 dp 的题目用记忆化搜索写起来真的十分方便。
我们来想这么一个问题:如何简单的表示一个数中所有数字出现个数的奇偶性这一状态?
你可能会说,状压呗。先用一个二进制数
那我们要从哪里开始优化呢?
首先,一个数的状态最多只有 3 种,要么没出现,要么出现了偶数次,要么出现了奇数次。所以说,我们可以用一个三进制状态来保存。而刚刚那种做法相当于是四进制,就十分浪费。采用优化的算法后,计算量约为
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int a[20], n, p[10];
inline int get_state(int state, int x) {
if (state / p[x] % 3 == 2) state -= p[x];
else if (state / p[x] % 3 == 1) state += p[x];
else state += p[x];
return state;
}
inline bool check_state(int state) {
for (int i = 0; i < 10; ++i)
if (state / p[i] % 3) {
int y = state / p[i] % 3;
if ((y & 1) == (i & 1)) return 0;
}
return 1;
}
LL f[20][59049][2];
LL dfs(int pos, int state, bool limit, bool zero) {
if (pos == 0) return check_state(state);
if (!limit && ~f[pos][state][zero]) return f[pos][state][zero];
LL res = 0;
for (int i = 0; i <= (limit ? a[pos] : 9); ++i)
res += dfs(pos - 1, zero && !i ? 0 : get_state(state, i), limit && (i == a[pos]), zero && !i);
if (!limit) f[pos][state][zero] = res;
return res;
}
LL calc(LL X) {
n = 0;
while (X) a[++n] = X % 10, X /= 10;
return dfs(n, 0, 1, 1);
}
int main() {
int T;
memset(f, -1, sizeof f);
p[0] = 1;
for (int i = 1; i < 10; ++i) p[i] = p[i - 1] * 3;
scanf("%d", &T);
while (T--) {
LL L, R;
scanf("%lld%lld", &L, &R);
printf("%lld\n", calc(R) - calc(L - 1));
}
}