hdu 4507 吉哥系列故事——恨7不成妻(数位DP,5级)

吉哥系列故事——恨7不成妻

Time Limit: 1000/500 MS (Java/Others)    Memory Limit: 65535/32768 K (Java/Others)
Total Submission(s): 957    Accepted Submission(s): 300


Problem Description
  单身!
  依然单身!
  吉哥依然单身!
  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]中和7无关的数字的平方和,并将结果对10^9 + 7 求模后输出。
 

Sample Input
 
  
3 1 9 10 11 17 17
 

Sample Output
 
  
236 221 0
 

Source
 

Recommend
liuyiding

思路:维护3个值,个数,总和,平方和。
          第一个是与7无关的数的个数,就是简单的数位DP了,很常规
          第二个与7无关的数的和的维护需要用到第一个个数。
           处理到第pos个数位时,加上i*10^pos * 后面的个数
          第三个的维护需要用到前面两个
          (pre*10^pos + next)^2= (pre*10^pos)^2+2*pre*10^pos*next +next^2

#include<cstdio>
#include<cstring>
#include<iostream>
#define FOR(i,a,b) for(int i=a;i<=b;++i)
#define clr(f,z) memset(f,z,sizeof(f))
#define LL __int64
#define ll(x) (1<<x)
using namespace std;
const LL mod=1e9+7;
class Edge
{ public:
  LL sqrsum;///平方和
  LL cnt;//个数
  LL sum;//和
}dp[40][33][33];//位数,1,0;
LL p[21];
int bit[40],pos;
LL L,R;
Edge DP(int pp,int sb,int ss,bool big)//位和%7,和%7
{ Edge tmp,ret;
  if(pp==0)
  {
    tmp.cnt=(sb!=0&&ss!=0);
    tmp.sqrsum=tmp.sum=0;
    return tmp;
  }
  if(big&&dp[pp][sb][ss].cnt!=-1)
    return dp[pp][sb][ss];
  int kn=big?9:bit[pp];
  tmp.cnt=tmp.sqrsum=tmp.sum=0;
  FOR(i,0,kn)
  {
    if(i==7)continue;//不能含7
    ret=DP(pp-1,(sb+i)%7,(ss*10+i)%7,big||kn!=i );
    tmp.cnt=(tmp.cnt+ret.cnt)%mod;
    tmp.sum=(tmp.sum+ret.sum+ret.cnt*((p[pp-1]*i)%mod) )%mod;
    tmp.sqrsum=(tmp.sqrsum+ret.sqrsum+(((2*p[pp-1]*i)%mod)*ret.sum)%mod)%mod;
    tmp.sqrsum=(tmp.sqrsum+(ret.cnt*(i*p[pp-1]%mod))%mod*(i*p[pp-1])%mod)%mod;
  }
  if(big)dp[pp][sb][ss]=tmp;
  return tmp;
}
LL get(LL x)
{ pos=0;
  while(x)
  {
    bit[++pos]=x%10;
    x/=10;
  }
  LL ret=0;
 return DP(pos,0,0,0).sqrsum;
}
int main()
{ int cas;
  p[0]=1;p[1]=10;
  FOR(i,2,20)
  p[i]=(p[1]*p[i-1])%mod;//防溢出
  while(~scanf("%d",&cas))
  {
    FOR(ca,1,cas)
    {
      FOR(i,0,30)FOR(j,0,22)FOR(k,0,22)
      dp[i][j][k].cnt=-1;
     // cin>>L>>R>>K;
      scanf("%I64d%I64d",&L,&R);
      LL ans=(get(R)-get(L-1)+mod)%mod;
      printf("%I64d\n",ans);
      //cout<<get(R)-get(L-1)<<endl;
    }
  }
  return 0;
}


转载于:https://www.cnblogs.com/nealgavin/p/3797602.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值