B - Investigating Div-Sum Property
题目描述:
例题6 数字和与倍数
给定正整数a,b,k 你的任务是在所有满足a<=n<=b的整数n中,统计有多少个满足n自身是k的倍数,且n的各个数字(十进制)之和也是k的倍数?比如当k=7时,322满足条件,因为322和3+2+2都是7的整数倍。当a=1,b=1000,k=4时,有64个整数条件。
输入格式:
输入第一行为测试数据组数T(T<100)。以下T行每行3个整数a,b,k(1<=a<=b<=231, 1<=k<=1000)
输出格式:
对于每组数据,输出满足条件整数的个数
输入数据:
3
1 20 1
1 20 2
1 1000 4
输出数据:
20
5
64
思路:
不难想到用f(n)来表示 0~n中满足条件的个数,那么答案就是 f(b)−f(a−1)f(b)-f(a-1)f(b)−f(a−1)
可以用加法原理
比如3212可以表示为
0***
1***
2***
30**
31**
320*
3210
3211
3212
*代表该数字为0~9中的其中一个
用 f(d,m1,m2)f(d,m1,m2)f(d,m1,m2)来表示d个数字,十进制数位之和取余k等于m1,值取余k等于m2。
那么 $ f (d+1,m1,m2)=\sum_{x=0}^{9} f ( d, (m1-x)%k,(m2-10^d)%k)$
当然 f(3,0,0)就值0~999中满足条件的个数
然后模拟将数字分解为上述情况,记下每种情况的前缀值和十进制位数的前缀和,计算出该情况下满足条件的个数就好了
举个例子:
0*** 用f(3,0,0)
1*** 用f(3,-1+k%k,-1000%k)
下面的就不列举了
注意int行十进制位数之和最大位9*9+1=100 所以不可能被大于100的k取余,所以当k>100的时候只有0符合条件
#include<cstdio>
#include<algorithm>
#include<stack>
#include<queue>
#include<cstring>
#include<string>
#include<iostream>
#include<iomanip>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int maxn=10000;
const int branch=26;
const int inf=0x3f3f3f3f;
const int MOD=1e6+7;
int k;//取余k
int f[12][105][105];
int get_mod(int n,int mod);
void init()
{
mset(f,0);//f[d][m1][m2] 要求 d>=1且 m1
f[0][0][0]=1;//初始条件
int ppw=1;
for(int i=1;i<=9;++i)
{
for(int m1=0;m1<k;m1++)
{
for(int m2=0;m2<k;++m2)
{
int sum=0;
for(int x=0;x<=9;++x)//枚举第i位数字
{
sum+=f[i-1][get_mod(m1-x,k)][get_mod(m2-x*ppw,k)];
}
f[i][m1][m2]=sum;
}
}
//记录 10^(i-1)
ppw*=10;
}
}
int get_mod(int n,int mod)
{
return (n%mod+mod)%mod;
}
int solve(int num)//求0~num中有多少个数满足 取余k
{
if(num==0)
return 1;
int ans=0;
int top=0,pos[10];
int nn=num;
while(nn)
{
pos[top++]=nn%10;
nn/=10;
}
int sum=0;//记录前缀的值
int ppw=1;//记录权值
int ss=0;//记录前缀的市值仅为之和
for(int i=0;i<top-1;++i)
ppw*=10;
for(int i=top-1;i>=0;--i)
{
for(int j=0;j<pos[i];++j)
{
ans+=f[i][get_mod(-1*ss-j,k)][get_mod(-1*sum-ppw*j,k)];//组合数学加法原理
}
sum+=ppw*pos[i];
ss+=pos[i];
ppw/=10;
}
ans+=f[0][get_mod(-ss,k)][get_mod(-sum,k)];
return ans;
}
int main()
{
int t,a,b,ans;
scanf("%d",&t);
while(t--)
{
scanf("%d %d %d",&a,&b,&k);
if(k>100)
{
printf("0\n");
continue;
}
init();//根据k 算出 0~9位数都有多少个
ans=solve(b)-solve(a-1);
printf("%d\n",ans);
}
return 0;
}