[SPOJ BALNUM - Balanced Numbers]数位DP
题目链接:[SPOJ BALNUM - Balanced Numbers]
题意描述:求区间
[L,R]
中有多少个数字
x
满足
解题思路: 裸的数位DP,状态转移中需要维护的状态有:当前位的位置,0~9中每个数字是否出现,若出现,出现奇数次还是偶数次,还有一个就是前导零。
记录0~9中每个数字的状态,可以用一个三进制的数字进行状态压缩。0表示没有出现过,1表示出现奇数次,2表示出现偶数次。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int BITS = 20;
const int DIGS = 10;
const int STATES = 59048 + 5; ///2222222222 = 59048
int T;
ULL L, R;
LL dp[BITS][STATES];
int dig[BITS];
int G[] = {1, 3, 9, 27, 81, 243, 729, 2187, 6561, 19683};
///9876543210
///2121212121 true
inline bool check(int x) {
int rem;
for(int i = 0; i < 10; i++) {
rem = x % 3;
x /= 3;
if(rem == 0) continue;
if((i & 1) ^ (rem & 1) == 0) return false;
}
return true;
}
/// 0 未选 1 奇数 2 偶数
inline int slip(int x, int pos) {
int ret = 0, rem;
for(int i = 0; i < 10; i++) {
rem = x % 3;
x /= 3;
if(pos == i) {
ret += G[i] * (rem & 1 ? 2 : 1);
} else {
ret += G[i] * rem;
}
}
return ret;
}
LL dfs(int pos, int status, bool limit, bool zero) {
if(pos < 1) return check(status);
if(!limit && dp[pos][status] != -1) return dp[pos][status];
int last = limit ? dig[pos] : 9;
LL ret = 0;
for(int i = 0; i <= last; i ++) {
ret += dfs(pos - 1, zero && i == 0 ? 0 : slip(status, i), limit && (i == last), zero && i == 0);
}
if(!limit) dp[pos][status] = ret;
return ret;
}
LL solve(ULL x) {
LL ret = 0;
int len = 0;
while(x) {
dig[++ len] = x % 10;
x /= 10;
}
ret = dfs(len, 0x00, true, true);
return ret;
}
int main() {
#ifndef ONLINE_JUDGE
freopen("input.txt", "r", stdin);
#endif // ONLINE_JUDGE
memset(dp, -1, sizeof(dp));
scanf("%d", &T);
while(T --) {
scanf("%I64u %I64u", &L, &R);
printf("%I64d\n", solve(R) - solve(L - 1));
}
return 0;
}