题意
求出[x, y] 范围内的平衡数,平衡数定义为:以数中某个位为轴心,两边的数的偏移量为矩,数位权重,使得整个数平衡。
思路
外层枚举平衡点,然后数位DP即可。设计状态: dp[pos][o][left_right] 表示处理到当前pos位,一开始枚举o点为支点,前pos-1位左边减右边的权值是left_right的数的个数。
注意:1.减法可能为负,所以需要加一个位移偏量,这里我设为2000;2.对于一个正数,如果它是 Balanced Number,那么它有且只有一个平衡点。但是计算0时枚举所有的支点都会把他算在内一次,重复计算了(位数-1)次。所以最后结果要减去。
PS:外层枚举支点要比内层枚举支点剪枝效果更好。
代码
[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;
const int oo = 0x7fffffff;
VI num;
LL dp[20][20][4000];
LL dfs(int pos, int o, int left_right, bool limit){
if (pos == -1) return (left_right == 2000);
if (!limit && ~dp[pos][o][left_right]) return dp[pos][o][left_right];
int end = limit?num[pos] : 9;
LL res = 0;
for (int i = 0; i <= end; i ++){
res += dfs(pos-1, o, left_right+i*(o-pos), limit && (i==end));
}
if (!limit) dp[pos][o][left_right] = res;
return res;
}
LL cal(LL x){
if (x < 0) return 0;
num.clear();
while(x){
num.push_back(x%10);
x /= 10;
}
MEM(dp, -1);
LL ans = 0;
for (int i = 0; i < (int)num.size(); i ++){
ans += dfs(num.size()-1, i, 2000, 1);
}
return ans - num.size() + 1;
}
int main(){
//freopen("test.in", "r", stdin);
//freopen("test.out", "w", stdout);
int t;
scanf("%d", &t);
while(t --){
LL x, y;
scanf("%I64d %I64d", &x, &y);
printf("%I64d
", cal(y) - cal(x-1));
}
return 0;
}
[/cpp]