分析
看到题的第一反应,存储前面 n 位的值乘坐标,然后暴力检查,然而时间复杂度虽是可的,但没有正确性,我们发现对于同一个值,可能有多种可能性,比如第一位为三,第二位为一和第一位为三,第二位为二以此种方法计算出的值是相同的,但很明显他们对应的方案不一样。
因此我们需要考虑一种对于一个计算出的值,我们能百分之百保证它的 dp 中存的方案数是正确的,所以我们考虑确定支点的位置,可以发现在你确定了支点之后,无论你怎么弄前面的 n 位,后面的方案数一定是确定的,做个类比,你之前的不确定支点位置的情况下,左右移动端点会影响方案数,但在确定了支点后这种情况就避免掉了,所以对于一个数,我们枚举第一位到最后一位作为支点,在数位 dp 过程中,力矩加值为该位乘上支点下标减去该位置下标,小于零时排除掉,所以我们 pos 从一开始,这样就可以先为正值,后面变为负值时就可以放心排除了。
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int dp[20][2500],tot,sw[25],a,b,qz[20],ss[20];
int dfs(int pos,int zd,int lim,int he){//位置,支点,上限,力矩和
if(pos==tot+1)return he==0;
if(he<0)return 0;
if(!lim&&dp[pos][he]!=-1)return dp[pos][he];
int sum=0,mx=lim?sw[tot-pos+1]:9;
for(int i=0;i<=mx;i++){
sum+=dfs(pos+1,zd,lim&&(i==mx),he+i*(zd-pos));
}
if(!lim)return dp[pos][he]=sum;
return sum;
}
int search(int x){
if(x==0)return 1;
tot=0;
while(x){
sw[++tot]=x%10;
x/=10;
}
memset(dp,-1,sizeof(dp));
int sum=0;
for(int i=1;i<=tot;i++){//枚举支点
memset(dp,-1,sizeof(dp));
sum+=dfs(1,i,1,0);
}
return sum-tot+1;
}
signed main()
{
cin>>a>>b;
cout<<search(b)-search(a-1);//前缀思想
return 0;
}