数位dp,终于守得云开见月明了。建议初学者先试试两道比较简单的hdu2089,hdu3555。
鸣谢:http://blog.csdn.net/acm_cxlove/article/details/8707084。
数位dp也是一种基于状态压缩、优化的动态规划。不同的是,它的压缩和优化往往基于数的一些特性。而数最基本的表现形式:a/b --- [a/b]、[a%b]。
这种dp才是体现一个人智慧的地方。(额外想为ACM竞赛的同学说两句,个人还是特别顶复旦出题的,至少它出的绝大部分题目都是可以自己通过大学以前的数学知识慢慢想到,而不是像某些学校的题目,只要听说过、学过、看过,某一个不知道哪里冒出来的数学理论就能瞬间AC,否则***。还是不放水了,言归正传)
本题,中文描述的,不解释了。初学者往往都会往容斥的方面想,(也不是不可以)但其实条件2、条件3的综合比较困难了,代码量和复杂度可能都会很大。
所以直接递归找跟7有关的数比较科学,假设原数字a+b位,如果搜索前a位fa(fa后b位都是0),后面b位跟7有关的任何一个数x,产生的结果和是(fa+x)^2+x^2。对所有的x,就有{fa^2*cnt(x)+2fa*sum(x)+sum(x^2)=f(a+b,b)}+f(b,0)。这个很容易想到,那么如果搜索时刚好按a从小到大拆分呢?不就是很明显了吗?
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
#define LL long long
const LL mod = 1000000007LL;
#define mp(a,b) make_pair(a,b)
int bit[];
//长度,是否有7,数字和%7,数字%; 数字和、结果和、数字个数
long long s1[][][][],s2[][][][],cnt[][][][];
LL fac[]={};
typedef pair<pair<LL,LL>,LL> pll;
pll DP(int len,int a,int b,int c,int g){
//printf("len = %d, %d, %d, %d, g = %d\n",len,a,b,c,g);
if(g && cnt[len][a][b][c] >= )
return mp(mp(cnt[len][a][b][c],s1[len][a][b][c]),s2[len][a][b][c]);
if(len <= ){
if(b&&c&&!a) cnt[][a][b][c]=;
else cnt[][a][b][c]=;
s1[][a][b][c]=s2[][a][b][c]=;
return mp(mp(cnt[len][a][b][c],s1[len][a][b][c]),s2[len][a][b][c]);
}
int bound=bit[len]; if(g) bound=;
LL tcnt=,ts1=,ts2=;
int nl=len-,na,nb,nc;
for(int i=;i<=bound;i++){
na=(a||i==); nb=(b+i)%; nc=(c*+i)%;
pll p=DP(nl,na,nb,nc,g||(i<bound));
LL f = fac[nl]*i % mod; //f按位拆分,不用靠递归记录!!!
tcnt= (tcnt+p.first.first)%mod;
ts1 = (ts1+p.first.second+p.first.first*f)%mod;
ts2 = (ts2+p.first.first*(f*f%mod)%mod+p.first.second*f*%mod+p.second)%mod;
}
if(g){
cnt[len][a][b][c] = tcnt;
s1[len][a][b][c] = ts1;
s2[len][a][b][c] = ts2;
}
return mp(mp(tcnt,ts1),ts2);
}
LL sum(LL n){
LL a=n,b=n+,c=*n+;
LL x=,y=;
if(a%x==) a/=x,x=;if(a%y==) a/=y,y=;
if(b%x==) b/=x,x=;if(b%y==) b/=y,y=;
if(c%x==) c/=x,x=;if(c%y==) c/=y,y=;
a%=mod;b%=mod;c%=mod;
return (a*b%mod)*c%mod;
}
LL solve(LL n){
if(n <= ) return ;
int len=;
memset(bit,,sizeof(bit));
LL m=n;
while(n > )
bit[++len]=n%, n/=;
//cout<<"n = "<<m<<" ,len = "<<len<<endl;
return ((sum(m)-DP(len,,,,).second)%mod+mod)%mod;
}
int main()
{
for(int i=;i<;i++)
fac[i]=(fac[i-]*)%mod;
memset(cnt,-,sizeof(cnt));
int cases; cin>>cases;
for(int cas=;cas<=cases;cas++){
LL l,r;
cin>>l>>r;
//scanf("%lld%lld",&l,&r);
cout<<((solve(r)-solve(l-))%mod+mod)%mod<<endl;
}
return ;
}