BZOJ1799: [Ahoi2009]self 同类分布——数位dp

题目

【题目描述】
给出 $ a,b $,求出 $ [a,b] $ 中各位数字之和能整除原数的数的个数。
【输入格式】
输入只有一行,包含两个正整数 $ a,b $。
【输出格式】
输出只有一行,包含一个整数,表示答案。
【样例输入】
10 19
【样例输出】
3
【数据范围与提示】
对于 $ 10 \% $ 的数据,$ a,b \leq 10^6 $;
对于 $ 30 \% $ 的数据,$ a,b \leq 10^9 $;
对于 $ 60 \% $ 的数据,$ a,b \leq 10^{12} $;
对于 $ 100 \% $的数据,$ 1 \leq a,b \leq 10^{18} $。

题解

测试时不会数位dp,打表 $ 30 \% $ 的悲伤故事

记 $ f[i][j][k][flag] $ 到第 $ i $ 位,剩下的数位和为 $ j $,前 $ i $ 的余数为 $ j $,$ flag $ 表示前 $ i $ 位有没有顶满的方案数

考虑转移

当前 $ i $ 没有顶满时, $ f[i][j][k][0]=\sum_{l=1}^{min(k,9)}f[i-1][j-l][(k*10+l)\%P][0] $,$ P $ 为每位数之和

如果前 $ i $ 顶满时,$ f[i][j][k][0]=\sum_{l=1}^{min(k,a[i])}f[i-1][j-l][(k*10+l)\%P][0] $,$ a[i] $ 表示 $ n $ 的第 $ i $ 位的值

将 $ b $ 的 dp 值减去 $ a-1 $ 的 dp 值即可

代码

 1 #include<bits/stdc++.h>
 2 #define LL long long
 3 using namespace std;
 4 const int N=1e5+5;
 5 int len,a[20];
 6 LL l,r,f[20][200][200][2],ans;
 7 LL dfs(int pos,int sum,int res,int P,bool fl){
 8     if(!~pos) return !sum&&!res;
 9     if(~f[pos][sum][res][fl])return f[pos][sum][res][fl];
10     int s=min(fl?a[pos]:9,sum);LL num=0;
11     for(int i=0;i<=s;i++)
12         num+=dfs(pos-1,sum-i,(res*10+i)%P,P,fl&&(i==a[pos]));
13     return f[pos][sum][res][fl]=num;
14 }
15 LL work(LL n){
16     memset(a,0,sizeof a);
17     ans=0,len=0;
18     for(LL i=n;i;i/=10)a[len++]=i%10;
19     for(int i=1;i<=9*len;i++)
20         memset(f,-1,sizeof f),ans+=dfs(len-1,i,0,i,1);
21     return ans;
22 }
23 int main(){
24     scanf("%lld%lld",&l,&r);
25     printf("%lld\n",work(r)-work(l-1));
26     return 0;
27 }
View Code

##DTOJ 3762##

猜你喜欢

转载自www.cnblogs.com/chmwt/p/10584786.html