XDU1161(一本通1588)
思路:
用二维数组dp[i][j]表示数字取到i位的情况下,各位数字之和对题中所给N取模为j的数字个数。例如对于样例来说,dp[1][0]表示数字最多可取到1位,各个位之和对9取模为0个数字个数,这样的数字有两个,即0和9。
类似地,dp[1][1]=1,因为1%9=1;dp[1][2]=1,因为2%9=2;dp[2][0]=11,因为0,9,18,27,36,45,54,63,72,81,90,99满足条件。注意这里dp数字的dp[i][j],i是指数字的总位数可以是1,2,.....i,而不是只算位数是i的数字。
可以先预处理一下dp数组,假如模数为mod,先用一个for循环赋值dp[1][0]到dp[1][mod-1]
for(int i=0;i<=9;i++) dp[1][i%mod]+=1;
然后用i可以递推i+1的情况,如果已经知道dp[i][j]的值,现在将数字位数可取值增加到i+1,第i+1位上的数字可以取0到9,假如取的是y,由于之前i位之和取模后是j,加上这个y之后,总共的i+1位之和取模后应该是(j+y)%mod,所以dp[i+1][(j+y)%mod]+=dp[i][j]。
求取区间[a,b]之间符合条件的数字个数,可以当作求0到b之间符合的个数减去0到a-1之间符合条件的个数。
要求0到X中间符合条件的数字个数,先把X每一位存到数组shu[13]中,例如X=387,求0到387之间,对9取模符合要求的个数。
387被存储为 shu[1]=7,shu[2]=8,shu[3]=3;(这个顺序和数字读起来刚好相反了)。
假如要暴力枚举的算,可以发现,当数字的最高位取其最大值时,低位的取值是受限制的,最高位取2时,十位上最多只能取到8,但如果最高位取1或者2,后两位是任意取0到9之间。所以,对于最高位取值val<shu[3]时,这里就是1或者2,对答案的贡献就是dp[i-1][(mod-val%mod)%mod],这种写法是因为最高位取得了val,其余位置之和加起来,再和val之和取模mod为0时符合条件,例如最高位取2,剩余位置之和对mod取模为7时,再加上这个2,再对mod取模为0,符合条件。
所以最高位取1,或者2时,对答案贡献 dp[1][8]和dp[2][7]。
最高位取0时,低的两位也都是可以取0到9之间任意值的,这时对答案贡献dp[2][0],
最高位取3时,就开始出现限制,这时第二位只能取0到8。而取8时最低位也受到限制只能取0到7。这里再分一次情况:
第二位取0到7时,最低为可以任意取0到0,并且由于最高位对mod取模为3,其余各位之和需要对mod取模为6,所以第二位取0到7时,对答案贡献为dp[1][6],dp[1][5],dp[1][4],dp[1][3],dp[1][2],dp[1][1],dp[1][0],dp[1][8]。
第二位取8时,最低位可以取0到7,对于取到最低位的情况,可以直接把各个位置之和加起来,取模为0则答案加1。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll N,M,mod;
ll shu[13];
ll dp[13][110];
int cnt;
void init()
{
memset(dp,0,sizeof(dp));
for(ll i=0;i<=9;i++)
dp[1][(i%mod)]+=1;
for(ll i=2;i<=12;i++)
for(ll j=0;j<=9;j++)
for(ll k=0;k<mod;k++)
dp[i][(j+k)%mod]+=dp[i-1][k];
}
void bian(ll n)
{
cnt=0;
while(n)
{
shu[++cnt]=n%10;
n/=10;
}
}
ll ac(ll n)
{
if(n<10)
{
if(n%mod==0)return 1;
else return 0;
}
bian(n);
ll ans=0;
ans+=dp[cnt-1][0];//这是最高位取0时,前cnt-1位可以任意取0到9之间
for(ll i=1;i<shu[cnt];i++)//最高位取1到shu[cnt]之间,前cnt-1位也可任意取
{
ans+=dp[cnt-1][(mod-i%mod)%mod];
}
ll he=shu[cnt];
for(ll i=cnt-1;i>=1;i--)
{
for(ll j=0;j<shu[i];j++)
{
ll need=(mod-(he+j)%mod)%mod;
if(i>1)ans+=dp[i-1][need];
else
{
if((he+j)%mod==0)ans++;
}
}
he+=shu[i];
}
if(he%mod==0)ans++;
return ans;
}
int main()
{
while(scanf("%lld %lld %lld",&N,&M,&mod)!=EOF)
{
init();
printf("%lld\n",ac(M)-ac(N-1));
}
return 0;
}