传送门
SOL
“数字之和整除原数”
·············一股浓厚的小学数学的气息。。
递推:
第一维 :位数 第二维:个位数字总和
最开始可能会想去推点数论结论方便直接转移,发现只有位数 和 总和 难以实现”拼数“
突然发现 取模 可以表示除法的结果 ,
把数字总和看成mod,两个数模数相加 等于 mod 即 整除 (这个好像是常规操作。。)
所以在后面递归拼数的过程中
原始思路方程 :
(预处理中)
f [ i ] [ j ] [ S u m ] [ l ] = ∑ p = 0 9 f [ i − 1 ] [ j − p ] [ S u m ] [ ( l − p ∗ 1 0 i − 1 ) m o d S u m ] f[i][j][Sum][l] = \sum_{p=0}^9 f[i-1][j-p][Sum][(l-p*10^{i-1}) mod Sum] f[i][j][Sum][l]=p=0∑9f[i−1][j−p][Sum][(l−p∗10i−1)modSum]
i:第几位 j:数字总和 sum:模数 l:原数取模的余数
发现第三维 sum明显 可以 压掉 (不同sum之间单独存在)
(主要是四维的话,空间也开不下。。)
接下来是拼数的方程:
t ::
a
+
b
+
c
a+b+c
a+b+c, q :
a
b
c
‾
\overline{abc}
abc
m
o
d
S
u
m
modSum
modSum
表示前面已经产生的总和
转移时:
i f ( p < m a x N u m ) r e t + = f [ i − 1 ] [ S u m − t − p ] [ ( S u m − q − p ∗ 1 0 i − 1 ) m o d S u m ] if(p<maxNum)ret+= f[i-1][Sum-t-p][(Sum-q-p*10^{i-1})modSum ] if(p<maxNum)ret+=f[i−1][Sum−t−p][(Sum−q−p∗10i−1)modSum]
i f ( p = = m a x N u m ) t + = p ; q + = ( p ∗ 1 0 i − 1 ) m o d S u m if(p==maxNum) t+=p;q+=(p*10^{i-1})mod Sum if(p==maxNum)t+=p;q+=(p∗10i−1)modSum
记忆化:
直接按数字总和枚举即可
(必须要固定数字总和,否则无法表示原数除总和的结果)
CODE
#include<bits/stdc++.h>
#define pf printf
#define sf scanf
#define int long long
using namespace std;
int f[20][200][200],fc[20];
int mod,bit[20],top=0;
inline int chn(int k){
return (k%mod+mod)%mod;
}
inline void pre(){
memset(f,0,sizeof f);
f[0][0][0]=1;
for(int i=1;i<=top;++i){
for(int j=0;j<=mod;++j){
for(int l=0;l<=mod;++l){
for(int p=0;p<=9;++p){
if(j<p)break;
f[i][j][l]+=f[i-1][j-p][chn(l-p*fc[i-1])];
}
}
}
}
}
inline int work(int R){
int ret=0;
top=0;
while(R)bit[++top]=R%10,R/=10;
for(mod=1;mod<=9*(top-1)+bit[top];++mod){
pre();
int t=0,q=0;
for(int loc=top;loc>=1;--loc){
if(mod<t)break;
int i;
for(i=0;i<bit[loc];++i){
if(mod-t<i)break;
ret+=f[loc-1][mod-t-i][chn(mod-q-i*fc[loc-1])];
}
i=bit[loc];
q=(q+i*fc[loc-1]%mod)%mod;t+=i;
}
ret+=(q%mod==0&&t==mod);
}
return ret;
}
signed main (){
int l,r;sf("%lld%lld",&l,&r);
fc[0]=1;
for(int i=1;i<=18;++i)fc[i]=fc[i-1]*10;
pf("%lld",work(r)-work(l-1));
return 0;
}