题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3709
题目大意:定义平衡数:以一个数的某一位为支点,其左右各个位分别乘以力矩的和是相等的。比如4139这个数,我们以3为力矩,左边的力矩和为4*2+1*1=右边的力矩和9*1。下载给出一个区间[l,r](0<=l<=r<=10^18),找出区间内有多少个平衡数。
分析:数位DP。我们用dp(i,j,k)来表示以第j位为支点的力矩和为k时的i位数有多少个,这样我们在枚举支点的过程中分别深搜。
实现代码如下:
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
ll dp[25][25][2005];
int dig[25],len;
ll dfs(int pos,int o,int l,int lim) //o为支点,l为力矩
{
if(pos<0) return l==0;
if(l<0) return 0; //力矩和为负,说明支点选靠前了,没必要继续考虑了
if(!lim&&dp[pos][o][l]!=-1) return dp[pos][o][l];
int num=lim?dig[pos]:9;
ll ans=0;
for(int i=0;i<=num;i++)
{
int nex=l;
nex+=(pos-o)*i;
ans+=dfs(pos-1,o,nex,lim&&(i==num));
}
if(!lim) dp[pos][o][l]=ans;
return ans;
}
ll count(ll x)
{
len=0;
while(x)
{
dig[len++]=x%10;
x/=10;
}
memset(dp,-1,sizeof(dp));
ll ans=0;
for(int i=0;i<len;i++) //枚举每一位作为支点
ans+=dfs(len-1,i,0,1);
return ans-(len-1); //排除0,00,000这些情况
}
int main()
{
int t;
ll l,r;
scanf("%d",&t);
while(t--)
{
scanf("%I64d%I64d",&l,&r);
printf("%I64d\n",count(r)-count(l-1));
}
return 0;
}