题目大意:定义一个平衡数为某个数选定其中一位为支点之后,两边的每一位乘上力矩之后的和相等,问区间【a,b】里有多少个平衡数;
思路:看样子就是个数位DP。。orz
首先,如果能证明出对于每个数来说,如果它是一个平衡数,那这个数只能有一个支点能使得它能是一个平衡数,即对于每个数来说,支点的解最多只有一个(0个的时候表示它无论如何都不能是一个平衡数);
如果我们能证明出这个优美的性质,那么就可以通过枚举支点所在位来确定以当前位为支点的时候,长度为len的解的个数;
下证对于每个数来说最多只有一个支点:
假设每一个数位上的数字为a[i],一共有n位,支点的位置为p,力矩长度为i,则一个数是平衡数时有下列等式;
然后就是往下推了
这样整理后发现左边是a[i]*i的形式,右边是a[i]*p的形式,都少了支点的那一项(a[p]*p),其实就是下面这样
即若一个数为平衡数时,它满足下面这个式子:
假设这个式子存在另一个解q,即存在一个q!=p使得
那么就有:
整理一下会变成
又因为a[i]是大于等于0的,所以要想这个式子成立,要么n位全是0,但显然不能有前导0,全是0只有n==1且a[1]==1的时候,所以只能是p-q==0,即p==q,故支点不可能存在两个解,然后就可以放心枚举每一个支点了;
具体代码跟普通的数位DP差别不大(把支点前的位乘力矩长加进去,支点后的位乘力矩减掉);
因为在枚举支点的时候,每一次枚举都会出现00,000,0000这些情况,在数位DP的时候会它当成合法情况,所以要减去这些实际上不合法的情况。
如有错误请斧正。。orz
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int,int> pp;
LL dig[22];
LL dp[22][22][4000];
int getdig(LL n)
{
int cnt=0;
while(n>0)
{
dig[++cnt]=n%10;
n/=10;
}
return cnt;
}
LL dfs(int len,LL pr,LL diot,bool limit)
{
if(len==0)
{
return pr==0;
}
if(pr<0)
{
return 0;
}
if(!limit&&dp[len][diot][pr]!=-1)
{
return dp[len][diot][pr];
}
LL ans=0;
LL tot=limit?dig[len]:9;
for(int i=0 ; i<=tot ; i++)
{
LL t=pr+i*(len-diot);
ans+=dfs(len-1,t,diot,limit&&i==tot);
}
if(!limit)
{
dp[len][diot][pr]=ans;
}
return ans;
}
LL work(LL n)
{
if(n<0)
return 0;
int len=getdig(n);
LL res=0;
for(int i=1 ; i<=len ; i++)
{
res+=dfs(len,0,i,true);
}
return res-(len-1);
}
int main()
{
int T;
scanf("%d",&T);
memset(dp,-1,sizeof(dp));
while(T--)
{
LL n,m;
scanf("%lld %lld",&n,&m);
printf("%lld\n",work(m)-work(n-1));
}
return 0;
}