题意:如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
1、整数中某一位是7;
2、整数的每一位加起来的和是7的整数倍;
3、这个整数是7的整数倍;
现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。
分析:数位DP,关键内容如下。
(pre0+i)%7用于处理各个位数之和时候为7的倍数,(pre1*10+i)%7用于处理这个数是否为7的倍数。
#include<iostream>
using namespace std;
const __int64 mod=1000000007;
struct Node
{
__int64 c; //和7无关的数的个数
__int64 sum; //和7无关的数的和
__int64 sqsum; //和7无关的数的平方和
};
Node dp[20][10][10]; //分别表示待处理数的位数,各位数字和是7的倍数,数本身是7的倍数
int bit[20]; //存储待处理数的每位
__int64 p[20]; //p[i]=10^i,p[0]=1
void init()
{
int i,j,k;
p[0]=1;
for(i=1;i<20;i++)
p[i]=(p[i-1]*10)%mod;
for(i=0;i<20;i++)
for(j=0;j<10;j++)
for(k=0;k<10;k++)
dp[i][j][k].c=-1;
}
Node DFS(int pos,int pre0,int pre1,bool fg)
{
Node ans,tmp;
int end,i;
if(pos==-1) //数位已处理完,并判断pre0及pre1
{
tmp.c=pre0 && pre1;
tmp.sum=tmp.sqsum=0;
return tmp;
}
if(!fg && dp[pos][pre0][pre1].c!=-1) //注意边界
return dp[pos][pre0][pre1];
end=fg?bit[pos]:9;
ans.c=ans.sum=ans.sqsum=0;
for(i=0;i<=end;i++)
{
if(i==7) continue; //跳过7,有7即不合法
tmp=DFS(pos-1,(pre0+i)%7,(pre1*10+i)%7,fg&&i==end);
ans.c+=tmp.c;
ans.c%=mod;
ans.sum+=(tmp.sum+((p[pos]*i)%mod)*tmp.c%mod)%mod;
ans.sum%=mod;
ans.sqsum+=(tmp.sqsum+((2*p[pos]*i)%mod)*tmp.sum)%mod;
ans.sqsum%=mod;
ans.sqsum+=((tmp.c*p[pos])%mod*p[pos]%mod*i*i%mod);
ans.sqsum%=mod;
}
if(!fg) dp[pos][pre0][pre1]=ans; //要判断边界
return ans;
}
__int64 cal(__int64 n)
{
int pos=0;
while(n)
{
bit[pos++]=n%10;
n/=10;
}
return DFS(pos-1,0,0,true).sqsum;
}
int main()
{
int T;
__int64 l,r,ans;
scanf("%d",&T);
init();
while(T--)
{
scanf("%I64d%I64d",&l,&r);
ans=cal(r);
ans-=cal(l-1);
ans=(ans%mod+mod)%mod; //注意这里的写法,否则会出错,因为减法可能会出现负数
printf("%I64d\n",ans);
}
return 0;
}
HDU ACM 4507 吉哥系列故事——恨7不成妻 ->数位DP
原文地址:http://blog.csdn.net/a809146548/article/details/45851855