SPOJ BALNUM ★(位压缩状态+数位DP)

题意

求区间[A,B]上的平衡数个数。平衡数是这样的数:在数的各个位上,奇数数字出现偶数次,偶数数字出现奇数次。

思路

很明显我们需要记录每一位出现的次数。分别记录是不明智的,而我们又只需要记录奇数次或者偶数次即可。所以我们可以用一个<=1024的数state表示0~9这10个数字出现的次数奇偶性,当奇数出现偶数次则相应位为1,当偶数出现奇数次相应位为1,最后判断是不是1023。但是这样它的初试状态不好确定。很明显初始时我们需要把state赋值成1010101010,即682(0次算偶次)。但是如果某些偶数位从始至终都没有出现,那么那一位最终为0显然不合理。我也没有想到什么更好的办法,只能再加一个1024的vis数表示0~9这10个数字是否出现过,但这样空间不够。后来想到一个优化是我们不必记录奇数是否出现过,因为它们初始就为1。所以可以把vis缩减到32,这样空间就足够了~问题边迎刃而解。

代码

 

[cpp] #include <iostream> #include <cstdio> #include <cmath> #include <algorithm> #include <string> #include <cstring> #include <vector> #include <set> #include <stack> #include <queue> #define MID(x,y) ((x+y)/2) #define MEM(a,b) memset(a,b,sizeof(a)) #define REP(i, begin, m) for (int i = begin; i < begin+m; i ++) using namespace std; typedef long long LL; typedef vector <int> VI; typedef set <int> SETI; typedef queue <int> QI; typedef stack <int> SI; LL dp[20][1050][35][2]; VI num; LL dfs(int pos, int state, int vis, bool flag, bool limit){ if (pos == -1){ bool ok = 1; for (int i = 0; i <= 9; i ++) if ((state & (1<<i)) == 0){ if (i % 2) {ok = 0; break;} else if ((vis & (1 << (i/2))) != 0){ ok = 0; break; } } return ok; } if (!limit && ~dp[pos][state][vis][flag]) return dp[pos][state][vis][flag]; int end = limit?num[pos]:9; LL res = 0; for (int i = 0; i <= end; i ++){ int next_state, next_vis; if (i == 0 && !flag){ if (pos == 0){ next_state = state | 1; next_vis = vis|1; } else{ next_state = state; next_vis = vis; } } else{ next_state = state ^ (1<<i); if (i % 2 == 0) next_vis = vis|(1<<(i/2)); else next_vis = vis; } res += dfs(pos-1, next_state, next_vis, (!flag)&&i==0?flag:true, limit && (i==end)); } return limit?res:dp[pos][state][vis][flag] = res; } LL cal(LL x){ num.clear(); while(x){ num.push_back(x%10); x /= 10; } int len = num.size(); return dfs(len-1, 682, 0, 0, 1); } int main(){ //freopen("test.in", "r", stdin); //freopen("test.out", "w", stdout); MEM(dp, -1); int t; scanf("%d", &t); while(t --){ LL a, b; scanf("%lld %lld", &a, &b); printf("%lld\n", cal(b)-cal(a-1)); } // for (int i = 100; i <= 1000; i ++){ // if (cal(i)-cal(i-1) == 1) // printf("i = %d\n", i); // } return 0; } [/cpp]

你可能感兴趣的:(poj)