题意
给出 a , b a,b a,b,求出 [ a , b ] [a,b] [a,b]中各位数字之和能整除原数的数的个数。
数据范围
1 ≤ a ≤ b ≤ 1 0 18 1 ≤ a ≤ b ≤ 10^{18} 1≤a≤b≤1018
思路
(以下参考自洛谷)
(数位DP的思想所在,从数字位置出发填坑去判断是否满足条件)
这题状态的选取也是很有意思,一般的状态都有当前位pos,前一位pre,原数st,这里数字范围太大了,不能直接做下标,所以考虑了取模状态mod。
这里模数(各位之和)肯定要有。问题是模数怎么求得?
一开始考虑过最后搜索到叶子的时候,记录原数各位之和,好想并不能这么干,搜索过程中sum是变化的。
所以决定暴力枚举所有可能的数字和sum。 18位最多有
9
∗
18
=
162
9*18=162
9∗18=162个。
搜到叶子了,需判断余数等于0并且所有数字和等于设定的sum,满足即计数1。
eq 表示pos位之前的数是否都一样,如果都一样,pos位的值就受限,否则可以填0~9。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll dp[20][200][200];
int a[20], sum;
//记搜模板
ll dfs(int eq, int pos, int cur, int mod)
{
if(cur > sum) return 0; //当前和 > 已设定和 剪枝
if(!pos) return mod==0 && cur==sum; // 已经到最低位了
if(!eq && ~dp[pos][cur][mod]) //搜过了
return dp[pos][cur][mod];
int limit = (eq) ? a[pos] : 9; //pos位受限制
ll res = 0;
for(int i = 0; i <= limit; i++)
res += dfs(eq&&(i==limit), pos-1, cur+i, (mod*10+i)%sum);
if(!eq) //eq=0,表示pos前的位置不全相等,没有最高位限制
dp[pos][cur][mod] = res;
return res;
}
ll solve(ll n)
{
int len = 0; // len 表示数字的长度
while(n)
{
a[++len] = n%10; // a数组存储n的各位数字,下标从1开始
n /= 10;
}
ll res = 0;
for(int i = 1; i <= len*9; i++)
{
sum = i; //枚举全局变量sum
memset(dp, -1, sizeof(dp));
res += dfs(1, len, 0, 0);
}
return res;
}
int main()
{
ll l, r;
cin >> l >> r;
cout << solve(r) - solve(l-1) << endl;
return 0;
}
开启我的数位dp第一题~