题目链接:Problem - 3709
题意:给定区间[a,b],求区间内平衡数的个数。所谓平衡数即有一位做平衡点,左右两边数字的力矩相等。
分析,这道题我们直接对平衡点进行枚举即可,然后分别求出来每个平衡点对应的平衡数求和,最后即为答案。
需要注意的是前缀0的情况,对于000000000这样的答案我们实际上是不应该计入答案的,也就是说我们在数位DP的过程中需要考虑前导0,在pos=0最后返回的时候也要判断当前是否有前导0的存在,如果有前导0就直接返回0,否则返回当前平衡数左边和右边的力矩差与0判等的bool值。这里需要注意的一点是由于我们在数位DP中没有考虑前导0,但这会导致我们少考虑一个数,那就是0,所以如果l~r内包含0,我们还应该加上一个1,对应到代码中就是若x<0,则在solve函数中直接返回-1。
当然我们也可以在数位DP过程中不考虑前导0,而在最后统一减去前导0的贡献,也就是ans-pos+1
下面分别附上两种方法的代码:
数位DP过程中考虑前导0:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
const int N=33;
ll f[N][N][5203];//f[i][j][k]表示当前遍历到第i位,支点为第j位,左边比右边多k的平衡数
int a[N];
ll dfs(int pos,int pivot,int sub,int lead,int limit)
{
if(sub<0) return 0;//sub是先变大后变小,由于一开始是0,所以当sub小于0时说明之后sub也是变小所以不符合题意,直接剪枝
if(pos==0)
{
if(!lead)
return sub==0;
else
return 0;
}
if(!lead&&!limit&&f[pos][pivot][sub]!=-1) return f[pos][pivot][sub];
int up=limit?a[pos]:9;
ll ans=0;
for(int i=0;i<=up;i++)
ans+=dfs(pos-1,pivot,sub+(pos-pivot)*i,lead&&(i==0),limit&&(i==up));
if(!lead&&!limit) f[pos][pivot][sub]=ans;
return ans;
}
ll solve(ll x)
{
if(x<0) return -1;
ll ans=0,pos=0;
while(x)
{
a[++pos]=x%10;
x/=10;
}
for(int pivot=1;pivot<=pos;pivot++)
ans+=dfs(pos,pivot,0,1,1);
return ans;
}
int main()
{
int T;
cin>>T;
ll l,r;
memset(f,-1,sizeof f);
while(T--)
{
scanf("%lld%lld",&l,&r);
printf("%lld\n",solve(r)-solve(l-1));
}
return 0;
}
数位DP过程中不考虑前导0:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
typedef long long ll;
int a[23];
ll dp[20][20][5200];
ll dfs(int pos,int o,int f,int flag)
{
if(pos==0) return f==0;
if(dp[pos][o][f]!=-1&&flag) return dp[pos][o][f];
ll ans=0;
int x=flag?9:a[pos];
for(int i=0;i<=x;i++) ans+=dfs(pos-1,o,f+(pos-o)*i,flag||i<x);
if(flag) dp[pos][o][f]=ans;
return ans;
}
ll solve(ll x)
{
int pos=0;
while(x)
{
a[++pos]=x%10;
x/=10;
}
ll ans=0;
for(int i=1;i<=pos;i++)
ans+=dfs(pos,i,0,0);
return ans-pos+1;
}
int main()
{
int T;
cin>>T;
ll l,r;
memset(dp,-1,sizeof dp);
while(T--)
{
scanf("%lld%lld",&l,&r);
printf("%lld\n",solve(r)-solve(l-1));
}
return 0;
}