杠杆数/平衡数【数位dp】
传送门
题目描述
如果把一个数的某一位当成支点,且左边的数字到这个点的力矩和等于右边的数字到这个点的力矩和,那么这个数就可以被叫成杠杆数。
比如4139就是杠杆数,把3当成支点,我们有这样的等式:4 * 2 + 1 * 1 = 9 * 1。
给定区间[x,y],求出在[x,y]中有几个杠杆数。
输入格式
两个数,表示x,y。
输出格式
一个输出,表示区间[x,y]中杠杆数的个数。
输入输出样例
输入 #17604 24324输出 #1897说明/提示
对于40%的数据,x<=y<=x+100000
对于100%的数据,1<=x<=y<=10^18
思路
数位dp,也叫试填法,一般应用于
求出在给定区间[A,B]内,符合条件P(i)的数i的个数.
条件P(i)一般与数的大小无关,而与 数的组成 有关(需要对一个数的每一位操作,因此数可能会很大甚至用字符串输入)
对于本题来说,相当于判断每个数字是否满足条件,可以用数位dp存储前缀有多少个满足条件的数字,用记忆化搜索保存
代码
#include<bits/stdc++.h> using namespace std; #define ll long long ll T,dp[20][20][2005],num[205],l,r; //dp[pos][x][num1]没有lim限制时,当前填到第pos位,支点为x,两边的贡献之和为num1时,有多少数字满足题意 ll dfs(int pos,int x,bool lim,int num1) { if(!pos) return num1==0;//边界 if(!lim && dp[pos][x][num1]!=-1) return dp[pos][x][num1];//没有限制才能直接返回 int maxx; if(!lim) maxx=9; else maxx=num[pos]; ll ans = 0; for(int i=0;i<=maxx;i++) ans += dfs(pos-1,x,lim&&i==maxx,num1+(x-pos)*i); if(!lim) dp[pos][x][num1]=ans; return ans; } ll solve(ll x)//[0,x]区间满足条件的数字数量 { if(x == -1)return 0; ll ans=0;int cnt=0; while(x) num[++cnt]=x%10,x/=10; for(int i=1;i<=cnt;i++) ans+=dfs(cnt,i,1,0); return ans-cnt+1;//00000000(所有位置都为0被重复算了cnt次,有一次满足题意) } int main() { // scanf("%lld",&T); memset(dp,-1,sizeof(dp)); // while(T--) { scanf("%lld%lld",&l,&r); printf("%lld\n",solve(r)-solve(l-1)); //类似前缀和 } return 0; }