1799: [Ahoi2009]self 同类分布
Time Limit: 50 Sec Memory Limit: 64 MBSubmit: 2455 Solved: 1124
[Submit][Status][Discuss]
Description
给出a,b,求出[a,b]中各位数字之和能整除原数的数的个数。
Input
Output
Sample Input
10 19
Sample Output
3
HINT
【约束条件】1 ≤ a ≤ b ≤ 10^18
显然数位dp
一开始想的是设$dp[pos][num][sum][mod][limit]$表示满足各位数字之和为sum,原数模num为mod的pos位数的个数
然后空间复杂度20*200*200*200*2就爆掉了
然后发现其实对于第二维,每一次dfs的时候它都不会改变,一直传递下去
于是我们不妨省去这一维,每次改变num前将dp数组重新赋值为-1,再进行下一个num的dfs
转移方程:$dp[pos][sum][mod][limit]=∑dp[pos][sum-i][(mod-i*10pos-1)%num][limit&(i==lim[pos])]$
思路还是挺自然的......
#include<bits/stdc++.h> using namespace std; long long A,B; int lim[20],cnt; long long dp[20][200][200][2]; long long pow_10[20],ans; long long dfs(int pos,int num,int sum,int ys,bool limit) { if(sum<0) return 0; if(!pos && sum>0) return 0; if(dp[pos][sum][ys][limit]>-1) return dp[pos][sum][ys][limit]; if(!pos) return 0; long long ret=0; int r=limit?lim[pos]:9; for(int i=0;i<=r;++i) ret+=dfs(pos-1,num,sum-i,((ys-pow_10[pos-1]*i)%num+num)%num,limit&(i==lim[pos])); dp[pos][sum][ys][limit]=ret; return ret; } void part(long long x,bool on) { cnt=0; while(x) { lim[++cnt]=(x%10); x/=10; } for(int i=1;i<=cnt*9;++i) { memset(dp,-1,sizeof(dp)); dp[0][0][0][0]=dp[0][0][0][1]=1; if(!on) ans+=dfs(cnt,i,i,0,1); else ans-=dfs(cnt,i,i,0,1); } } int main() { pow_10[0]=1; for(int i=1;i<=18;++i) pow_10[i]=(pow_10[i-1]*10); scanf("%lld%lld",&A,&B); part(B,0); part(A-1,1); printf("%lld",ans); return 0; }