J - 吉哥系列故事――恨7不成妻(HDU4507)

J - 吉哥系列故事――恨7不成妻

单身!
  依然单身!
  吉哥依然单身!
  DS级码农吉哥依然单身!
  所以,他生平最恨情人节,不管是214还是77,他都讨厌!
  
  吉哥观察了214和77这两个数,发现:
  2+1+4=7
  7+7=72
  77=7
11
  最终,他发现原来这一切归根到底都是因为和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
分析 :

当然,看完博客还是懵逼,因为我一开始的思路是错的。

首先这道题的状态是很好想的

d p [ p o s ] [ P r e _ s u m ] [ P r e _ P r o d u c t % 7 ] dp[pos][Pre\_sum][Pre\_Product\%7] dp[pos][Pre_sum][Pre_Product%7] 代表未知的pos位的前缀十进制位之和取余7为Pre_sum,前缀的值取余7为Pre_Product。

举个例子:54****对应的状态是 d p [ 4 ] [ 9 % 7 ] [ 54 % 7 ] dp[4][9\%7][54\%7] 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 = p o w ( 10 , p o s − 1 ) k=pow(10,pos-1) k=pow(10,pos1)

( i ∗ k + m j ) 2 = = ( i ∗ k ) 2 + m j 2 + 2 ∗ i ∗ k ∗ m j (i*k+m_j)^2==(i*k)^2+m_j^2+2*i*k*m_j (ik+mj)2==(ik)2+mj2+2ikmj

枚举这n个数在第pos为i的情况下,在该状态下后pos位满足条件的数的平方和为

P r o d u c t = n ∗ ( i ∗ k ) 2 + ∑ j = 1 n m j 2 + 2 ∗ i ∗ k ∗ ∑ j = = 1 n m j Product=n*(i*k)^2+\sum_{j=1} ^nm_j^2+2*i*k*\sum_{j==1}^nm_j Product=n(ik)2+j=1nmj2+2ikj==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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值