HDU-4507 吉哥系列故事――恨7不成妻 (数位dp)

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

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


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


分析:很容易的看出来是数位DP,然后很容易的看出来他的dp要有三维,即位置pos,数字之和sum,以及整个数字对7的模数num。最麻烦的就是平方和的计算。·一开始自信满满的声明了longlong的变量到pos等于0的时候计算这个数的平方。wa了4发才浪子回头。。数字的范围是1e18的QAQ。那相乘肯定爆longlong了。。难道用大数?(自信的朋友可以去试试).然后参考了kuangbin的做法。如何求很多数的平方和呢?如下

一个十进制数可以写成X=ΣAi*Pi,(其中Ai为X每一位的值,Pi=10^i)   456 = 4*100+5*10+6*1;
因为(A+B)^2=A*A+2*A*B+B*B
(X1+X2+X3+...+Xn)^2=X1^2+2*X1*(X2+X3+...+Xn)+(X2+X3+...+Xn)^2
=X1^2+2*X1*(X2+X3+...+Xn)+X2^2+2*X2*(X3+...+Xn)+(X3+...+Xn)^2

......这样递推下去就可以把一个数转换为如456^2  = (4*100+5*10+6*1)^2 ;234^2 = (2*100+3*10+4*1)^2;那么456^2+234^2 = 400^2+56^2+2*400*56+200^2+34^2+2*200*34 = (400^2+200^2)+2*(400*56+200*34)+(56^2+34^2(这个括号里面两位数的还可以继续往下递推))。

上面的栗子展示了平方和结合在一起如何计算的方法:

首先我们要有当前位i.... ans1 = (i*pi)^2;如400,200,

其次我们要当前位之后的数字之和su。,56,34. ans2 =2*(su*i*pi);

最后,因为我们是要计算多个数的平方和,因此在当前状态下肯定不止一个数字可以到达。所以还需要存储一个cnt计数。最终答案是cnt*(ans1+ans2).

综上:在dp时,记录当前状态合法数的数量cnt,当前状态之后的和su,平方和po

A=Ai*Pi

now.cnt=now.cnt+nxt.cnt;

now.su=now.su+nxt.su+A*nxt.cnt;//因为su是计算和的,所以su实际上已经是cnt个数的累加了。
now.po=now.po+A*A*nxt.cnt+2*A*nxt.su+nxt.po;

代码如下:

/* Author:kzl */
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>

using namespace std;
const int MOD = 1e9 + 7;
typedef long long LL;
int digit[20];
LL p[20];
struct My{
int cnt,su,po;
My(){}
My(int a,int b,int c):cnt(a),su(b),po(c){}
}dp[20][200][10];

My dfs(int pos,int sum,int num,int jud){
if(pos==0){
    My m;
    if(num==0||sum%7==0)m.cnt = 0;
    else m.cnt = 1;
    m.su = m.po = 0;
    return m;
}
if(!jud&&dp[pos][sum][num].cnt!=-1)return dp[pos][sum][num];
int sz = jud?digit[pos]:9;
My tmp,ans;
ans.cnt=ans.su=ans.po=0;
for(int i=0;i<=sz;i++)
{
    if(i==7)continue;
    tmp = dfs(pos-1,sum+i,(num*10+i)%7,jud&&(i==sz));
        ans.cnt+=tmp.cnt;
        ans.cnt%=MOD;
        ans.su+=(tmp.su+ ((p[pos-1]*i)%MOD)*tmp.cnt%MOD )%MOD;
        ans.su%=MOD;

        ans.po+=(tmp.po + ( (2*p[pos-1]*i)%MOD )*tmp.su)%MOD;
        ans.po%=MOD;
        ans.po+=( (tmp.cnt*p[pos-1])%MOD*p[pos-1]%MOD*i*i%MOD );
        ans.po%=MOD;
}
if(!jud)dp[pos][sum][num] = ans;
return ans;
}

My f(LL num)
{
    int pos = 0;
    while(num){
        digit[++pos] = num%10;
        num = num/10;
    }
    return dfs(pos,0,0,1);
}

int t;
LL l,r;
int main(){
    p[0] = 1;
    for(int i=1;i<20;i++)p[i] = (p[i-1]*10)%MOD;
   for(int i=0;i<20;i++)for(int j=0;j<200;j++)for(int k=0;k<10;k++)dp[i][j][k].cnt = -1;
    scanf("%d",&t);
    while(t--){
     scanf("%lld%lld",&l,&r);
    printf("%lld\n",(f(r).po-f(l-1).po+MOD)%MOD);
    }
return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值