这题用的是书上给的思路,不过,对于这种类型的题,做的太少,于是打算好好谢谢。不过,花了一个晚上,参考别人的代码写了遍。
这种类型的题一般都会用到前缀,设f(n)表示从1到n满足条件的数的个数。但是直接枚举,复杂度为O(n),但是由于范围最大能达到2^31左右,所以不能暴搞。
这里将一个数分成几部分来算:
f[d][m1][m2]表示除出数的第最高位的数的位数是d,各位数的和模k为m1,该数模k为m2的数字的个数。
f[d][m1][m2]=sum{f[d-1][(m1-x)%k][(m2-x*10^d-1)%k]|(0<=x&&x<=9)};
而每个数可以划分成一些f的和。
#include<iostream> #include<cstdio> #include<vector> #include<string> #include<queue> #include<cstring> #define maxn 10005 #define INF 0xfffffff #define mem(a,b) memset(a,b,sizeof(a)) #define FOR(i,s,t) for(int i=s;i<=t;i++) #define ull unsigned long long #define ll long long using namespace std; inline int rd(int &x) { char c=getchar(); while(!isdigit(c))c=getchar(); x=0; while(isdigit(c)) { x=x*10+c-'0'; c=getchar(); } return x; } int buf[10]; inline void ot(int i) { int p=0; if(i==0) p++; else while(i) { buf[p++]=i%10; i/=10; } for(int j=p-1; j>=0; j--) putchar('0'+buf[j]); } int f[15][100][10005];//数字的长度 各位数和模k 数模k int count(int x,int k) { if(x<10)//只有一位的时候m1和m2一样的 { return x/k; } int maxd=0,w=1;//记录最高位后有多少位 while(x/w>=10) { maxd++; w*=10; } int ret=0,m1=0,m2=0;//m1,m2分别记录当前位置的前面的数位和及数模k的余数 for(int i=maxd;i>0;i--)//后面至少有一个10 { int tmp=x/w; for(int j=0;j<tmp;j++) { ret+=f[i-1][((k-j-m1)%k+k)%k][((k-j*w-m2)%k+k)%k];//这里可以通过k直接减 } m1+=tmp; m2+=tmp*w; x-=tmp*w; w/=10; } //最后一位单独讨论 for(int i=0;i<=x;i++) { if(((k-i-m1)%k+k)%k==0&&((k-i-m2)%k+k)%k==0) ret++; } return ret-1;//为啥这里要减1,暂时还没搞懂 } int main() { int t,a,b,k; scanf("%d",&t); while(t--) { scanf("%d%d%d",&a,&b,&k); if(k>85) { printf("0\n"); } else { memset(f,0,sizeof(f)); //先求出所有的f[d][m1][m2]; int size=-1,sx=b;//记录最高位后面有多少个0 while(sx) { size++; sx/=10; } for(int i=0; i<=9; i++) // 最开始的时候只有1位的情况 f[0][i%k][i%k]+=1; int w=1; for(int i=1; i<=size; i++) { w*=10; for(int m1=0; m1<k; m1++) { for(int m2=0; m2<k; m2++) { for(int x=0; x<10; x++) { f[i][(m1+x)%k][((m2+x*w)%k+k)%k]+=f[i-1][m1][m2]; } } } } printf("%d\n",count(b,k)-count(a-1,k)); } } return 0; }
这里还有一个人写得也挺好的:http://blog.csdn.net/lenleaves/article/details/9104417
感觉前面的代码写得不好,我后面又用了两种方法写了下:
个人感觉还是记忆化搜索好理解,这题主要是在初始化的地方出了点错,下次得注意下。
记忆化搜索:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int f[15][100][10005]; int p[11]; void init(int x,int k) { memset(f,-1,sizeof(f)); int d=0; while(x)//计算有多少位 { d++; x/=10; } p[0]=1; for(int i=1; i<=d; i++) { p[i]=(p[i-1]*10)%k; } } int dp(int i,int m1,int m2,int k) { if(i==0) return (m1==0&&m2==0)?1:0;//这个地方得注意下,就是只有m1,m2 //都为0的时候才为1,否则其他的请款也得赋值,赋值为0 else if(f[i][m1][m2]>=0) return f[i][m1][m2]; int& ret=f[i][m1][m2]; ret=0; for(int x=0;x<10;x++) { ret+=dp(i-1,((m1-x)%k+k)%k,((m2-x*p[i-1])%k+k)%k,k); } return ret; } int a[11]; int count(int x,int k) { if(!x)//0得特判一下 { return 1; } int d=0; while(x) { a[d++]=x%10; x/=10; } int m1=0,m2=0,ret=0; for(int i=d-1; i>=0; i--) { if(!i) for(int j=0; j<=a[i]; j++) { ret+=dp(i,((k-j-m1)%k+k)%k,((k-j*p[i]-m2)%k+k)%k,k); } else { for(int j=0; j<a[i]; j++) { ret+=dp(i,((k-j-m1)%k+k)%k,((k-j*p[i]-m2)%k+k)%k,k); } } m1+=a[i]; m2+=a[i]*p[i]; } return ret; } int main() { int t,a,b,k; scanf("%d",&t); while(t--) { scanf("%d%d%d",&a,&b,&k); if(k>85) { printf("0\n"); } else { init(b,k); printf("%d\n",(count(b,k)-count(a-1,k))); } } return 0; }
递推:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int f[15][100][10005]; int p[11]; void init(int x,int k) { int d=0; while(x)//计算有多少位 { d++; x/=10; } p[0]=1; for(int i=1; i<d; i++) { p[i]=(p[i-1]*10)%k; } memset(f,0,sizeof(f)); f[0][0][0]=1; for(int i=1; i<d; i++) { for(int m1=0; m1<k; m1++) { for(int m2=0; m2<k; m2++) { for(int x=0; x<10; x++) { f[i][(m1+x)%k][(m2+x*p[i-1])%k]+=f[i-1][m1][m2]; } } } } } int a[11]; int count(int x,int k) { if(!x)//0得特判一下 { return 1; } int d=0; while(x) { a[d++]=x%10; x/=10; } int m1=0,m2=0,ret=0; for(int i=d-1; i>=0; i--) { if(!i) for(int j=0; j<=a[i]; j++) { ret+=f[i][((k-j-m1)%k+k)%k][((k-j*p[i]-m2)%k+k)%k]; } else { for(int j=0; j<a[i]; j++) { ret+=f[i][((k-j-m1)%k+k)%k][((k-j*p[i]-m2)%k+k)%k]; } } m1+=a[i]; m2+=a[i]*p[i]; } return ret; } int main() { int t,a,b,k; scanf("%d",&t); while(t--) { scanf("%d%d%d",&a,&b,&k); if(k>85) { printf("0\n"); } else { init(b,k); printf("%d\n",(count(b,k)-count(a-1,k))); } } return 0; }