bzoj 1799 [Ahoi2009]self 同类分布(数位DP)

题目:

bzoj 1799 [Ahoi2009]self 同类分布

解析:

 设 $f[loc][js][mod]$ 为第 $loc$ 位(从左往右),各位数和为 $js$ ,当前余数为 $mod$ 的数的个数

要求 $n$ 可以被各位数和整除,也就是 $n == 0 (mod js)$ 

这个题,$n$ 的最大位数为18,所以各位数和最大为 $sum<=9*18=162$ ,数据较小,可以直接枚举,对每一个 $sum$ 都记忆化搜索一遍,看 $js==sum$ 时有多少数满足条件,然后累加答案

ps:因为算 $(a,b)$ ,所以要用  $(1,b)-(1,a-1)$ 

代码:

 1 #include<bits/stdc++.h> 
 2 using namespace std;
 3 long long num[21],f[21][170][170],sum=0;//num负责统计n的每一位的值  
 4 long long dfs(bool xz,int loc,int js,int mod)
 5 //xz 代表这一位(指loc)是否到达上限(n在loc位的值) loc,js,mod和f[][][]中的意义一致 
 6 {    
 7     if(js>sum) return 0;                    
 8         //当累加答案超过sum,返回0 
 9     if(loc==0) return mod==0&&js==sum;
10         //当累加等于sum且余数为0,返回1 
11     if(xz==0&&~f[loc][js][mod]) return f[loc][js][mod];
12          //当f[][][]被查询过,直接返回 
13     int u=9;
14     if(xz==1) u=num[loc];                                
15         //达到上限,对下一位有限制 
16     long long ans=0;
17     for(int i=0;i<=u;++i)
18     {
19         ans+=dfs(xz&&(i==u),loc-1,js+i,(mod*10+i)%sum);
20     }
21     if(xz==0) f[loc][js][mod]=ans;
22     return ans;
23 }
24 long long work(long long x)
25 {
26     int len=0;
27     while(x)
28     {
29         num[++len]=x%10;
30         x/=10;
31     }                        //对 num[]的预处理 
32     long long ans=0;
33     for(int i=1;i<=len*9;++i)
34     {
35         memset(f,-1,sizeof(f));
36         sum=i;
37         ans+=dfs(1,len,0,0);
38     }                        //枚举无需多言 
39     return ans;
40 }
41 int main()
42 {
43     long long a,b;
44     cin>>a>>b;
45     cout<<work(b)-work(a-1); 
46     return 0;
47 }                
View Code

如果哪里出现问题或者讲的不够明白,请及时评论

原文地址:https://www.cnblogs.com/jasony/p/13276448.html