这道题居然提交了十次才过....期间小问题不断。思路的话基本是《训练指南》里面来的,不过有几个小问题需要注意一下。第一,当K在大于100的情况下,就直接输出0就可以了。因为a,b不超过2^31,可以估算出a,b最多十位十进制数,那么每位最大为9,所以各个数字之和是不可能超过100的,那么个数字之和为模K为0的条件是永远不可能到达的。
还有一点是,当剩余数字d=0时,当且仅当m1和m2都为0时,即f[0][0][0]为1,其余f[0][][]都为0。然后将已知的f[d][m1][m2]保存下来,递归求解即可。最后,书上提到在计算(m2-b)%k时若b很大m2-b小于0时怎么办。我的解决方法是,凡是遇到减法,则用式子(m2-b%k+k)%k计算(没有翻过书,可能有更简单的方法)。
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <cmath> #define MAX 32+5 #define MAXK 100+5 using namespace std; int f[MAX][MAXK][MAXK]; bool vis[MAX][MAXK][MAXK]; int T,k,A,B; char temp[64]; int p[MAX]; int recur(int,int,int); int main() { //freopen("data.txt","r",stdin); cin>>T; f[0][0][0]=1;//d为0时,只有m1,m2都为0,才为1 p[0]=1; for(int i=1;i<32;++i) p[i]=10*p[i-1]; while(T--){ cin>>A>>B>>k; if(k>100){//k>100结果必为0 cout<<0<<endl; continue; } A--; sprintf(temp,"%d",A);//整数转化为字符串 string a(temp); sprintf(temp,"%d",B); string b(temp); memset(vis,0,sizeof(vis)); int ans,pos=ans=0; int m1,m2=m1=0; while(pos<b.size()){ for(int i=0;i<b[pos]-'0';++i){ ans+=recur(b.size()-1-pos,(m1-i%k+k)%k,(m2-i*p[10,b.size()-pos-1]%k+k)%k); } m1=(m1-(b[pos]-'0')%k+k)%k; m2=(m2-(b[pos]-'0')*p[10,b.size()-pos-1]%k+k)%k; pos++; } ans+=f[0][m1][m2]; m1=m2=pos=0; while(pos<a.size()){ for(int i=0;i<a[pos]-'0';++i){ ans-=recur(a.size()-1-pos,(m1-i%k+k)%k,(m2-i*p[10,a.size()-pos-1]%k+k)%k); } m1=(m1-(a[pos]-'0')%k+k)%k; m2=(m2-(a[pos]-'0')*p[10,a.size()-pos-1]%k+k)%k; pos++; } ans-=f[0][m1][m2]; cout<<ans<<endl; } return 0; } int recur(int d,int m1,int m2) { if(vis[d][m1][m2]||d==0) return f[d][m1][m2]; f[d][m1][m2]=0; for(int i=0;i<10;++i){ f[d][m1][m2]+=recur(d-1,(m1+k-i%k)%k,(m2-i*p[10,d-1]%k+k)%k); } vis[d][m1][m2]=1; return f[d][m1][m2]; }
时间: 2024-10-30 01:21:20