题意:一个数是balanced number当且仅当它跟4139(位置编号3210)一样,当我们将pivot(不知是啥)的位置取在3的位置1时,左边4,1到3的距离和为4 * 2 + 1 * 1 = 9,右边9到3的距离9 * 1 = 9,左边与右边相等,所以称之为balanced number。求出[l,r]内这类数的个数(0<=l<=r<=1e18)
首先,明确某个数到pivot的距离为num * (pos- pivot),既然这个距离跟pivot有关,我们不妨枚举从0位开始向最高位枚举pivot的位置。此外,还需记录pivot位置前的距离和已经pivot位置后的距离和。但是这样并不好的状态并不好记录。于是我们考虑——相减!!!我们不妨设dp[i,j]为枚举到第i位时,pivot左边与右边的距离之差,而当枚举到最后一位,j==0时,它就是我们要找的balanced number!!
代码如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
ll dp[20][1500];
int digit[20];
ll dfs(int pos,ll sum,int piv,bool limit)
{
if(pos==-1) return !sum? 1:0;
if(!limit&&dp[pos][sum]!=-1) return dp[pos][sum];
ll ret=0;
int up= limit? digit[pos]:9;
for(int i=0;i<=up;++i)
{
if(sum+i*(pos-piv)<0) continue;
ret+=dfs(pos-1,sum+i*(pos-piv),piv,limit&&i==digit[pos]);
}
if(!limit) dp[pos][sum]=ret;
return ret;
}
ll solve(ll n)
{
int pos=0;
while(n)
{
digit[pos++]=n%10;
n/=10;
}
ll ans=0;
for(int i=0;i<pos;++i) //枚举piv的位置
{
memset(dp,-1,sizeof(dp));
ans+=dfs(pos-1,0,i,1);
}
return ans-pos+1; //ans-(pos-1),之所以这么做是因为我这个算法0会被重复数pos-1遍,
//所以要减去(debug的时候发现的)
}
int main()
{
int T;
cin>>T;
while(T--)
{
ll l,r;
cin>>l>>r;
ll ans=solve(r)-solve( l? l-1:l );
if(!l) ans++; //如果l==0,ans还要再+1,因为上面相减的时候0的贡献被去掉了
cout<<ans<<endl;
}
return 0;
}
小结:代码中有两点要注意:
①l有可能为0,为0的时候要特殊处理一下
②solve函数中ans要减去pos再加1,这是因为0被重复数了(pos-1)遍
另外,巧妙的利用相减,有时会很大的优化算法。