题意
很久很久以前,有一个数字加入了 UOJ 群。
这天,在它讨论“一个数字应该怎么取模”的时候一不小心被删除了,变成了被粉碎的数字。
突然间,它突然发现它忘记了自己是谁。这让它感觉很焦躁,于是它来拜托你来帮它找到它所代表的整数 x。
在经过一番询问之后,你整理了一下现在你得到的线索:
已知整数 R,有 1≤x≤R
定义函数 f(x) 为 x 的各位数字之和,例如 f(13)=4,f(233)=8。已知整数 k,有 f(x)=f(k×x)
然而遗憾的是,你发现即使拥有了这些条件,可能的 x 依然有很多个。因此,为了进一步缩小排查范围,你想要知道满足这两个条件的整数有多少个。
60%特殊的打表技巧
若我们一个一个算f(x)会只能得30分,但我们可以打
106
的表,那么计算时我们可以这样写:
f(x)=f(x/106)+f(xmod106)
;
这样便可以多拿30分了。
分析
数位dp..
感觉自己掌握的不够熟练,一出类似的题不能很快想出来。
f[i,sum,sum1,r,p]
为(从低位到高位)第i位,原数前i位的数字和为sum,原数乘上k的数的数字和为sum1,原数上次乘k的数的进位r,和这个数是否比R大,p,的数字的个数。
首先明显的乘法,所以只能从低到高(和上次的光棍不同。。)
而这个转移也是比较轻松的:
f[i+1][sum+chmod10][sum1+(k∗ch+r)mod10][(k∗ch+r)/10][p]+=f[i][sum][r][pd];
但是转移时间过大,所以我们只能继续优化:
我们发现我们最后要求的是sum=sum1,所以我们可以把它转化成差的形式:
f[i+1][sum+ch−(k∗ch+r)mod10][k∗ch+r)/10][p]+=f[i][sum][r][pd];
到此便可以很好解决问题了。
注意
但是求答案时我们却不能认为答案为 f[bit[0]][0][0] ,因为我们会发现这里依然会有进位,所以我们只能再算几次,把它的进位算完(多的几位,可以理解成这几位选0即可)再统计答案。
还有这题和之前的光棍不同,因为要乘法就要从低位算,而不用乘法的,可以从高位算,便可以直接统计答案,无需多算。
注意边界的计算,进位最多999,差最多20*10*2=400;
(请读者思考)
最后可能读者看完下面的程序后会认为f会多算一些比R要大的数,但是我们再统计答案时强制让数为小于等于R即p=0;
#include<cstdio>
#include<cstdlib>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
int k,bit[25],p,x,y;
long long f[25][410][1005][2],t;
int main(){
scanf("%lld%lld",&t,&k);
int po=0;
while (t!=0) bit[++po]=t%10,t=t/10;
f[0][200][0][0]=1;
fo(i,0,po+2)
fo(r,0,999)
fo(sum,0,400)
fo(pd,0,1)
if (f[i][sum][r][pd])
fo(ch,0,9){
if (ch>bit[i+1]) p=1;
else if (ch<bit[i+1]) p=0;
else p=pd;
x=(k*ch+r)/10;y=sum+ch-(k*ch+r)%10;
f[i+1][y][x][p]+=f[i][sum][r][pd];
}
printf("%lld",f[po+3][200][0][0]-1);=
}