这题用的是书上给的思路,不过,对于这种类型的题,做的太少,于是打算好好谢谢。不过,花了一个晚上,参考别人的代码写了遍。
这种类型的题一般都会用到前缀,设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.youkuaiyun.com/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;
}