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

  单身! 
  依然单身! 
  吉哥依然单身! 
  DS级码农吉哥依然单身! 
  所以,他生平最恨情人节,不管是214还是77,他都讨厌! 
   
  吉哥观察了214和77这两个数,发现: 
  2+1+4=7 
  7+7=7*2 
  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 L,R中和7无关的数字的平方和,并将结果对10^9 + 7 求模后输出。
Sample Input
3
1 9
10 11
17 17
Sample Output
236
221
0

思路

题目dp思路很好想,但麻烦的地方在于其要求的值是符合条件的树的平方和。 
我们用pra记录前面位的数字和%7,prb记录前面位的数字的值%7。加上遍历时对7的筛除,我们很容易可以找出与7无关的数,但是怎样求平方和呢?


我们用三个变量 
cnt表示当前状态下的与7无关的数的个数,在搜索的过程中很容易得到 
sum表示当前状态下的与7无关的数的合 
那么newsum = i*10^len*cnt + sum(i是当前选取的数,用cnt个加上cnt个数的和即sum,便是新的数的和) 
sqsum表示当前状态下与7无关的数的平方和 
(i*10^len + num)^2 = (i*10^len)^2 + 2*i*10^len*num + num^2; 
而cnt个数的平方和就是 
(i*10^len)^2*cnt + SUM(num^2) + 2*i*10^len*SUM(num) 
即(i*10^len)^2*cnt + sqsum + 2*i*10^len*sum。

主要是这个思路啊,一开始想的类似数位dp那样,保存一个当前数取模之后的一个数。然后最后返回这个数的平方。然而比标准答案总是多搜出来一部分。

这样的话其实dp值里面存的就不是数的个数了,而是这些数的平方和。然而这样其实缺少一个状态,那就是当前数模1e9加7之后的数是多少,不过很明显我们是不可能开出这么大的一个数组的。于是便去看了一下别人是怎么解的。

感觉完全想不出来啊,好难。。。。。。

找了一个比较详细且比较好理解的搬了过来。

#include <cstdio>
#include <cstring>
using namespace std;
typedef long long LL;
const LL mod = 1e9+7;
LL l,r;
LL p[20];
struct node
{
    LL cnt = -1;
    LL sum;
    LL sqsum;
    node(LL a, LL b, LL c)
    {
        cnt = a;
        sum = b;
        sqsum = c;
    }
    node(){};
}dp[20][7][7];
int digit[20];
node dfs(int len,int sum1,int sum2,bool up)
{
    if(len == -1)
    {
        return node(sum1 && sum2,0,0);
    }
    if(!up && dp[len][sum1][sum2].cnt != -1)return dp[len][sum1][sum2];
    node temp,ans(0,0,0);
    int n = up?digit[len] : 9;
    for(int i = 0 ; i <= n ; ++i)
    {
        if(i == 7)continue;
        temp = dfs(len - 1,(sum1+i)%7,(sum2*10+i)%7,up && i==n);
        ans.cnt = (ans.cnt + temp.cnt)%mod;
        ans.sum += (temp.sum + p[len]*i%mod*temp.cnt%mod)%mod;
        ans.sum %= mod;
        ans.sqsum += (temp.sqsum + ((2*p[len]*i)%mod)*temp.sum)%mod;
        ans.sqsum %= mod;
        ans.sqsum += ( (temp.cnt*p[len]) %mod*p[len]%mod*i*i%mod );
        ans.sqsum %= mod;
    }
    if(!up)dp[len][sum1][sum2] = ans;
    return ans;
}
LL cal(LL x)
{
    int len = 0;
    while(x)
    {
        digit[len++] = x % 10;
        x /= 10;
    }
    return dfs(len-1,0,0,1).sqsum;
}
int main()
{
    int t;
    scanf("%d",&t);
    p[0] = 1;
    for(int i = 1 ; i < 20 ; ++i)p[i] = (p[i-1]*10)%mod;
    for(int tt = 1 ; tt <= t ; ++tt)
    {
        scanf("%I64d%I64d",&l,&r);
        printf("%I64d\n",(cal(r) - cal(l-1) + mod) % mod);
    }
    return 0;
}






  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值