897
#include
#include
#include
int num[20], ln;
__int64 f[20][20][406];
__int64 g[20][91][406];
int maxs[20][20];
__int64 dfs(int i, int pt, int pres, int sum, bool flag){
int high, p;
__int64 ret;
if (i <= 0) return sum == 0 && pt;
if (sum < 0 || sum > 405) return 0;
if (!pt && sum > maxs[i][0]) return 0;
if (pt && sum > maxs[i][pt - i]) return 0;
if (pt && !flag && ~f[i][pt][sum]) return f[i][pt][sum];
if (!pt && !flag && ~g[i][pres][sum]) return g[i][pres][sum];
high = flag ? num[i] : 9;
ret = 0;
for (p = 0; p <= high; p++){
if (!pt){
ret += dfs(i - 1, pt, pres + p, sum + pres + p, flag && (p == high));
if (sum || p) ret += dfs(i - 1, i, 0, sum, flag && (p == high));
}else{
ret += dfs(i - 1, pt, 0, sum - p * (pt - i), flag && (p == high));
}
}
if (!flag && pt) f[i][pt][sum] = ret;
if (!flag && !pt) g[i][pres][sum] = ret;
return ret;
}
__int64 calc(char *a){
int i, j, la;
la = strlen(a);
for (i = 0; i < la && a[i] == '0'; i++);
for (j = la - 1; j >= i; j--)
// num[j - i + 1] = a[j] - '0';
num[la - j] = a[j] - '0';
ln = la - i;
return dfs(ln, 0, 0, 0, true) + 1;
}
bool Dec(char *a){
int t, la;
la = strlen(a);
t = la - 1;
while(t >= 0 && a[t] == '0'){
a[t] = '9';
t--;
}
if (t >= 0) a[t]--;
else return false;
return true;
}
int main(){
char a[20], b[20];
int k, T;
__int64 ans;
memset(f, -1, sizeof(f));
memset(g, -1, sizeof(g));
memset(maxs, 0, sizeof(maxs));
for (k = 1; k <= 20; k++){
maxs[k][0] = maxs[k - 1][1];
maxs[k][1] = maxs[k - 1][1] + 9 * k;
for (T = 2; T <= 20 - k; T++){
maxs[k][T] = maxs[k][T - 1] + 9 * k;
}
}
scanf("%d", &T);
for (k = 1; k <= T; k++){
scanf("%s %s", a, b);
if (!Dec(a)) ans = calc(b);
else ans = calc(b) - calc(a);
printf("%I64d\n", ans);
}
return 0;
}
/***************************
数位DP
经过上次经验,用记忆化搜索实现。
开始本能地会去想设俩个状态,前i位达到上限的,和后i位未达上限的,然后拼起来计算结果。
但是由于上次痛苦的教训,还是直接记忆化搜索,记录下搜索状态,来的简单,暴力,不容易错。。。
dfs(i, pt, pres, sum)
枚举第i位,此时轴是pt,
对于轴之前的i,pres = 前i位各位数和,sum = 各位数*距离的和
对于轴之后的i,pres 没用了直接设为0,sum 是还剩多少
也就说轴之前,sum是累加的,之后,sum就开始累减,能减到0,就是平衡的
注意,累加时,每向后移动一位,sum != sum * 2 + num[i], sum = sum + pres + num[i]
pt = 0, 表示轴还未确定,或者说轴在后i位中
本想直接记录f[i][pt][pres][sum], 结果果真MLE了...
记录f[i][pt][sum]呢? 会发现当pt = 0时,pres不同,会导致后面的结果不同,因为后的sum是依靠pres的
若判下pt!=0时,记录f[i][pt][sum],这样确实对,但丢了许多记录,最终TLE...
于是又想了各种剪枝,比如记录后i位能组成的最大和,若此时sum比它还大,显然不平衡之类的...
然而这只是杯水车薪,照样TLE...此后就在小心翼翼的MLE和TLE中交错度过...
最后,把pt = 0 和 pt != 0 的情况分开记录,因为pt = 0的时候需要记录pres,而pt != 0的不需要
这样,既没MLE,也没TLE,红红Accept从一堆绿中脱颖而出,有些刺眼...
想想也是,g记录轴之前的,f记录轴之后的,不记g的话,纯暴力枚举9位数甚至更多...
另外,把数字倒过来存,dfs时当flag=false时,值会与这数本身无关,
这样只要在一开始初始化f,g,往后就可以反复利用,不论输入怎么变
总之,跑31ms,比之前5000ms+,好的不是一星半点,还是挺满意的...
经杨大牛指点,在递归外枚举轴的位置,可以大大简化思维复杂度...
也不用考虑MLE什么的...
另一种判定思路,假设轴在最左边,计算力矩和sum,数字和pres,
考虑平衡度量bla = sum - 0, 注意到每向右移动一次轴,
相当于bla = (sum - x) - (0 + pres - x) = bla - pres
及,只要轴在最左边时,sum % pres == 0,即可判定数字balance
****************************/