题目链接: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; }