给定n∈[1,1e12],求1到n的所有整数中,各位数字之和能整除它本身的数的个数。
这道题与UVA-11361类似,假如设dp[u][lim][m1][m2]为枚举到第u位(从低到高数),是否受限,各位之和为m1,本身为m2时继续往下枚举能得到的答案数,可以得到正确的答案。但m2过大不能直接作为状态保存,如果对各位之和取模的话,又会发现dp的过程中模数是不确定的,怎么办?
解决方法是枚举模数,也就是枚举各位之和k,这样模数就固定了,就可以轻松地往下转移了。边界条件为u<0&&m1==k&&m2==0,状态转移方程见代码。
1 #include<bits/stdc++.h> 2 3 using namespace std; 4 typedef long long ll; 5 const ll N=12+2; 6 ll bit[N],nb,d[N][2][130][130],a,b,k; 7 ll dp(ll u,ll lim,ll m1,ll m2) { 8 if(u<0) { 9 if(m1==k&&m2==0)return 1; 10 return 0; 11 } 12 ll& ret=d[u][lim][m1][m2]; 13 if(~ret)return ret; 14 ret=0; 15 for(ll i=0; i<=(lim?bit[u]:9); ++i)ret+=dp(u-1,lim&&i==bit[u],m1+i,(m2*10+i)%k); 16 return ret; 17 } 18 19 ll getans(ll x) { 20 ll ans=0; 21 for(nb=0; x; x/=10)bit[nb++]=x%10; 22 for(k=1; k<=120; ++k) { 23 memset(d,-1,sizeof d); 24 ans+=dp(nb-1,1,0,0); 25 } 26 return ans; 27 } 28 29 int main() { 30 freopen("just.in","r",stdin); 31 freopen("just.out","w",stdout); 32 ll n; 33 scanf("%lld",&n); 34 printf("%lld ",getans(n)); 35 return 0; 36 }