题意:
定义一个数若以某个数字作为支点,左右力矩(到支点的距离乘数字大小)相等,则称这个数是平衡的。求区间中有多少个平衡数。
比如,以第二位为支点,左边的力矩是,右边的力矩是
思路:
这道入门题是我大意了(锤子入门题,不能秒解的题还是入门题?)
这道题求的答案时需记忆化搜索次,是的位数
,表示当前是第位,支点是第位,平衡值为,这个状态是能保证结果唯一的
看到状态就很明确要怎么做了,但是为什么这么做?
可以保证的是,某个数如果是平衡数,有且仅有一个支点(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; }