题目链接:hihoCoder 1033
解题思路:
思路超级简单,就是单纯的数位DP,但是,还是觉得好恶心。需要特殊考虑的就是第一位数字为0的情况,不能直接把状态转移的结果拿出来。
状态:dp[i][j][k+100],i 位数中以 j 开头的数交叉和为k的数量
(这里因为k有可能取负数,所以需要让它变正,加100即可)
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MOD 1000000007
#define mid 100
using namespace std;
long long L,R,M,res[20][10][205],dp[20][10][205];
long long base[20];
int cnt,tmp[20];
void cal(long long num)
{
cnt=0;
while(num)
{
tmp[++cnt]=num%10;
num/=10;
}
}
long long solve(long long num)
{
if(num==0)
return 0;
memset(dp,0,sizeof(dp));
memset(res,0,sizeof(res));
cal(num);
for(int i=0;i<10;i++)
res[1][i][i+mid]=i, dp[1][i][i+mid]=1;
for(int i=2;i<=cnt;i++)
{
for(int j=0;j<10;j++)
{
for(int k=0;k<10;k++)
{
for(int l=0;l<200;l++)
{
dp[i][j][j-l+2*mid] = (dp[i][j][j-l+2*mid] + dp[i-1][k][l])%MOD;
res[i][j][j-l+2*mid] = (res[i][j][j-l+2*mid] + res[i-1][k][l])%MOD;
res[i][j][j-l+2*mid] = (res[i][j][j-l+2*mid] + ((j*base[i])%MOD)*dp[i-1][k][l])%MOD;
}
}
}
}
long long ans=0,val=0,sum=0;
for(int i=1;i<cnt;i++)
{
for(int j=1;j<10;j++)
{
ans = (ans+res[i][j][M+mid])%MOD;
}
}
for(int i=cnt;i>=1;i--)
{
for(int j=0;j<tmp[i];j++)
{
if(i==cnt&&j==0)
continue;
if(i%2==cnt%2)
ans = (ans+(sum*dp[i][j][M-val+mid])%MOD+res[i][j][M-val+mid])%MOD;
else
ans = (ans+(sum*dp[i][j][val-M+mid])%MOD+res[i][j][val-M+mid])%MOD;
}
sum = ((base[i]*tmp[i])%MOD + sum)%MOD;
if(i%2==cnt%2)
val+=tmp[i];
else
val-=tmp[i];
}
return ans;
}
int main()
{
base[1]=1;
for(int i=2;i<=18;i++)
base[i]=(10*base[i-1])%MOD;
while(~scanf("%lld %lld %lld",&L,&R,&M))
{
printf("%lld\n",(solve(R+1) - solve(L) + MOD)%MOD);
}
return 0;
}
总结:
记住负数取模要先加模数!!!
千万记住0这种特殊情况!!!