题意:给定一个区间[a,b],求区间内的平衡数个数,平衡数指的是存在一个支点使数形成杠杆平衡的数。(0<=a<=b<=1e18)
支点是判断一个数是否平衡的重要因素,因此把支点当参数传下来,并压在dp数组中。每枚举一个数就算一次这个数乘力矩的值,累计在sum中,最后判断sum是否为0即可。
但是这种方法存在一个问题,零的支点可以放在每个位置,所以每枚举一个支点,零都被算了一次,所以返回值应减去(p-1),p为数的位数。
#include<iostream> #include<cmath> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define FOR(i,x,y) for(int i=(x);i<=(y);i++) #define DOR(i,x,y) for(int i=(x);i>=(y);i--) typedef long long LL; using namespace std; LL dp[20][20][2803]; int num[23]; LL dfs(int k,int sum,int &torque,bool ismax) { if(k==0)return sum==1400; if(!ismax && ~dp[k][torque][sum])return dp[k][torque][sum]; int maxer=ismax?num[k]:9; LL res=0; FOR(i,0,maxer) res+=dfs(k-1,sum+(k-torque)*i,torque,ismax&&i==maxer); if(!ismax)dp[k][torque][sum]=res; return res; } LL solve(LL x) { int p=0; //如果x是零时恰好返回1,所以输入时不需要特判 while(x) { num[++p]=x%10; x/=10; } LL res=0; FOR(i,1,p)res+=dfs(p,1400,i,1); return res-p+1; //特判0 } int main() { int T; scanf("%d",&T); memset(dp,-1,sizeof(dp)); while(T--) { LL A,B; scanf("%lld%lld",&A,&B); printf("%lld\n",solve(B)-solve(A-1)); } return 0; }