題目大意:定義一個平衡數爲某個數選定其中一位爲支點之後,兩邊的每一位乘上力矩之後的和相等,問區間【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;
}