题意:
定义一个数若以某个数字作为支点,左右力矩(到支点的距离乘数字大小)相等,则称这个数是平衡的。求区间中有多少个平衡数。
比如,以第二位为支点,左边的力矩是,右边的力矩是

思路:
这道入门题是我大意了(锤子入门题,不能秒解的题还是入门题?)

这道题求的答案时需记忆化搜索次,的位数

,表示当前是第位,支点是第位,平衡值为,这个状态是能保证结果唯一的

看到状态就很明确要怎么做了,但是为什么这么做?
可以保证的是,某个数如果是平衡数,有且仅有一个支点(0除外)
所以以每一位当支点算出来的总答案是不会重的(0除外)

比较特殊,不管以哪一位当支点它都是平衡数,所以总答案最后减去多算的0就是最终答案了。

心路历程:
没必要以0当支点,因为算出来只有0是平衡数

智商不在线,写出来
这样的剪枝,前面一部分是必须的,后面的我想当然的只考虑了后面的数位,意思是如果后面的数全部取最大都不能把减为0,那么后面的搜索是没有必要的。开始的时候写的还是,就离谱。

,还有这种操作。

还有这个操作:
图片说明

写成这样能不心累吗,都是一步步才发现写错了。

Code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include <vector>
#include<map>
#include<set>
#include<utility>
using namespace std;
typedef long long ll;
const int maxn = 1e5+7,maxm=2e5+7;
inline ll read() {
    ll s = 0, w = 1;
    char ch = getchar();
    while (ch < 48 || ch > 57) {
        if (ch == '-') w = -1;
        ch = getchar();
    }
    while (ch >= 48 && ch <= 57)
        s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}
ll dp[20][20][2000];
int digit[35],pos;
ll dfs(int len,int val,bool limit) {
    if(!len) return val==0;
    if(!limit&&dp[len][pos][val]!=-1) return dp[len][pos][val];
    int endi=(limit?digit[len]:9);
    ll ans=0;
    for(int i=0;i<=endi;++i) {
        if(val+i*(len-pos)<0) continue;
        ans+=dfs(len-1,val+i*(len-pos),limit&&i==endi);
    }
    if(!limit) dp[len][pos][val]=ans;
    return ans;
}
ll solve(ll x) {
    int len=0;
    ll ans=0;
    if(x<0) return 0;
    while(x) {
        digit[++len]=x%10;
        x/=10;
    }
    for(pos=len;pos;--pos) ans+=dfs(len,0,true);
    return ans-len+1;
}
int main() {
    ll l,r;
    memset(dp,-1,sizeof dp);
    int t=read();
    while(t--) {
        l=read(),r=read();
        printf("%lld\n",solve(r)-solve(l-1));
    }
    return 0;
}