J - 吉哥系列故事――恨7不成妻
单身!
依然单身!
吉哥依然单身!
DS级码农吉哥依然单身!
所以,他生平最恨情人节,不管是214还是77,他都讨厌!
吉哥观察了214和77这两个数,发现:
2+1+4=7
7+7=72
77=711
最终,他发现原来这一切归根到底都是因为和7有关!所以,他现在甚至讨厌一切和7有关的数!
什么样的数和7有关呢?
如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
1、整数中某一位是7;
2、整数的每一位加起来的和是7的整数倍;
3、这个整数是7的整数倍;
现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。
Input
输入数据的第一行是case数T(1 <= T <= 50),然后接下来的T行表示T个case;每个case在一行内包含两个正整数L, R(1 <= L <= R <= 10^18)。
Output
请计算[L,R]中和7无关的数字的平方和,并将结果对10^9 + 7 求模后输出。
Sample Input
3
1 9
10 11
17 17
Sample Output
236
221
0
参考博客:http://www.cnblogs.com/kuangbin/archive/2013/05/01/3053233.html
分析 :
当然,看完博客还是懵逼,因为我一开始的思路是错的。
首先这道题的状态是很好想的
dp[pos][Pre_sum][Pre_Product%7] 代表未知的pos位的前缀十进制位之和取余7为Pre_sum,前缀的值取余7为Pre_Product。
举个例子:54****对应的状态是 dp[4][9%7][54%7]
这个地方是我错的原因,不想看的可以跳过。
然后就开始数位dp加递归求解了吗?,合并一下就像这样?
ans+=dfs(pos-1,(PreSum+i)%7,PreProduct*10+i,istop&&i==endd);//这个合并的方法是错的
我们求的答案是满足条件的数的平方和,所以在进行状态合并的时时候相同状态对应的前缀可能是不同的,所以他们呢的平方和也是不同的,不能直接加上次计算结果。
就像23**与72**,对应的状态是一样的(先不考虑数位上存在7的条件),但是前者计算过了,在计算后者不能直接加前者计算的值。
所以我们应该寻求一个新的解法!
想一想,如果我们知道该状态下后pos位满足条件的数都是什么,我们应该怎么计算呢?
假如说第pos位是i,后pos-1位满足条件的数都已经知道有n个,对应的数字分别为m1,m2,m3… mn
那么后pos位中满足条件的平方和我们可以计算出来(这里的平方和中的数不包括前缀)。
设k位第pos位对应的权值 k=pow(10,pos−1)
(i∗k+mj)2==(i∗k)2+mj2+2∗i∗k∗mj
枚举这n个数在第pos为i的情况下,在该状态下后pos位满足条件的数的平方和为
Product=n∗(i∗k)2+∑j=1nmj2+2∗i∗k∗∑j==1nmj
枚举第pos位就得到后pos位满足条件的数的平方和,在上面的公式中用到了n(改状态下满足条件的数的个数),sum(该状态下满足条件的数的和),Preduct(改状态下满足条件的数的平方和)。
在合并的时候需要把这三个值全都统计一下,以便递归求解,最后的答案在Preduct里面。
#include<cstdio>
#include<algorithm>
#include<stack>
#include<queue>
#include<map>
#include<cmath>
#include<vector>
#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=1e5+10;
const int branch=26;
const int inf=0x3f;
const ll MOD=1e9+7;
struct node{//(针对于后pos位未知位数的)
ll cnt;//满足条件的个数
ll sum;//满足条件的数的和
ll Product;//Product为满足条件的数的平方和
node(){
cnt=-1;
sum=Product=0ll;
}
node(ll a,ll b,ll c){
cnt=a,sum=b,Product=c;}
};
node dp[25][10][10];//dp[pos][Pre_sum][Pre_Product%7];
int num[25];
ll quick_pow(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1)
ans=(ans*a)%MOD;
a=(a*a)%MOD;
b>>=1;
}
return ans;
}
node dfs(int pos,int PreSum,int PreProduct,bool istop)
{
if(!pos)
{
if(PreSum%7!=0&&PreProduct%7!=0)
return node(1,0,0);
else
return node(0,0,0);
}
if(!istop&&dp[pos][PreSum%7][PreProduct%7].cnt!=-1)
return dp[pos][PreSum%7][PreProduct%7];
int endd=istop?num[pos]:9;
node ans(0,0,0);
ll powval=quick_pow(10ll,pos-1);
for(int i=0;i<=endd;++i)
{
if(i==7)
continue;
//针对于每个位
node mid=dfs(pos-1,(PreSum+i)%7,(PreProduct*10+i)%7,istop&&i==endd);
ll k=i*powval%MOD;
ans.cnt+=mid.cnt;
ans.sum+=(mid.sum+(mid.cnt*k)%MOD)%MOD;
ans.Product+=(mid.Product+(2*k*mid.sum)%MOD+((mid.cnt*k)%MOD)*(k%MOD))%MOD;
ans.cnt%=MOD;
ans.sum%=MOD;
ans.Product%=MOD;
}
if(!istop)
dp[pos][PreSum%7][PreProduct%7]=ans;
return ans;
}
ll calc(ll val)
{
int pos=0;
do{
num[++pos]=val%10;
val/=10;
}
while(val);
return dfs(pos,0,0,1).Product;
}
int main()
{
int t;
ll a,b;
mset(dp,-1);
scanf("%d",&t);
while(t--)
{
scanf("%I64d%I64d",&a,&b);
printf("%I64d\n",(((calc(b)-calc(a-1))%MOD)+MOD)%MOD);
}
return 0;
}