BZOJ1799 [Ahoi2009]self 同类分布[数位DP]

求出[a,b]中各位数字之和能整除原数的数的个数。

困难的一道题。被迫看了题解:枚举每一个各位数字的和($<=162$),设计状态$f[len][sum][rest]$表示dp后面$len$位,要求这剩下的和是$sum$,并且其对$sum$取模是$rest$的方案数。

感觉也讲不出什么道理来,真的是。。经验问题啊。。。当时数位dp不太会,现在看来稍微好些了。或者也可以从最高位往后看,设前面填好的高位组成的各位和是sum,mod枚举剩rest,到最低位再检验正确性。

转移(向下一层dp)时就是把当前填的这一位数字从sum减掉,并且rest取$rest-i*10^{len-1}%p$,比如我一开始要求rest是0,第一位填3,后面的数要求的rest就变了。为什么是前面那个式子,这个可以推一下,很好推,就是剩余系相关的数学内容。然后每种sum都讨论一下,另外记搜可以带点剪枝什么的,卡卡就过啦。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<algorithm>
 6 #define dbg(x) cerr<<#x<<" = "<<x<<endl
 7 #define ddbg(x,y) cerr<<#x<<" = "<<x<<"   "<<#y<<" = "<<y<<endl
 8 using namespace std;
 9 typedef long long ll;
10 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;}
11 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;}
12 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
13 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
14 template<typename T>inline T read(T&x){
15     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
16     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
17 }
18 const ll bin[20]={0,1,10,1e2,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13,1e14,1e15,1e16,1e17,1e18};
19 ll l,r,f[19][164][164],p;
20 int flag,b[20];
21 inline int mod(ll x){return x<0?x+p:x;}
22 ll dp(int len,int s,int rest,bool limit){
23     if(!len){if(!s&&!rest)return 1;else return 0;}
24     if(s<0||9*len<s)return 0;//剪枝
25     if(!limit&&~f[len][s][rest])return f[len][s][rest];
26     int num=limit?b[len]:9;ll ret=0;
27     for(register int i=0;i<=num;++i)ret+=dp(len-1,s-i,mod(rest-i*1ll*bin[len]%p),limit&&(i==num));
28     return limit?ret:f[len][s][rest]=ret;
29 }
30 inline ll solve(ll x){
31     ll k=0,ret=0;while(x)b[++k]=x%10,x/=10;
32     for(p=1;p<=k*9;++p)memset(f,-1,sizeof f),ret+=dp(k,p,0,1);
33     return ret;
34 }
35 
36 int main(){//freopen("test.in","r",stdin);freopen("test.out","w",stdout);
37     read(l),read(r);if(l>=1e18)return printf("1\n"),0;if(r>=1e18)--r,++flag;
38     return printf("%lld\n",solve(r)-solve(l-1)+flag),0;
39 }

 

posted @ 2019-04-08 09:19  Ametsuji_akiya  阅读(133)  评论(0编辑  收藏  举报