HDOJ 4507 —— 数位DP

11 篇文章 0 订阅

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

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


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   |   We have carefully selected several similar problems for you:   4769  4768  4767  4766  4765 
 
挺明显的数位DP,学习了kuangbin大神的写法:http://www.cnblogs.com/kuangbin/archive/2013/05/01/3053233.html
思路是对应于题目的三个要求,我们去维护三个数:
1.与7无关的数                 
2.与7无关的数的和 --- 维护一个sum,每次加上p[pos] * i * 后面的数dfs
3.与7无关的数的平方和 ---维护一个sqsum,需要用到前面两个
(pre*10^pos + next)^2= (pre*10^pos)^2+2*pre*10^pos*next +next^2
/*
ID: xinming2
PROG: stall4
LANG: C++
*/
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <map>
#include <string>
#include <stack>
#include <cctype>
#include <vector>
#include <queue>
#include <set>
#include <utility>
#include <cassert>
using namespace std;
///#define Online_Judge
#define outstars cout << "***********************" << endl;
#define clr(a,b) memset(a,b,sizeof(a))
#define lson l , mid  , rt << 1
#define rson mid + 1 , r , rt << 1 | 1
#define mk make_pair
#define FOR(i , x , n) for(int i = (x) ; i < (n) ; i++)
#define FORR(i , x , n) for(int i = (x) ; i <= (n) ; i++)
#define REP(i , x , n) for(int i = (x) ; i > (n) ; i--)
#define REPP(i ,x , n) for(int i = (x) ; i >= (n) ; i--)
const int MAXN = 100000 + 50;
const int sigma_size = 26;
const long long LLMAX = 0x7fffffffffffffffLL;
const long long LLMIN = 0x8000000000000000LL;
const int INF = 0x7fffffff;
const int IMIN = 0x80000000;
#define eps 1e-8
const int mod = (int)1e9 + 7;
typedef long long LL;
const LL MOD = 1000000007LL;
const double PI = acos(-1.0);

typedef pair<int , int> pi;
#define Bug(s) cout << "s = " << s << endl;
///#pragma comment(linker, "/STACK:102400000,102400000")
struct Num
{
    LL cnt;///与7无关的个数
    LL sum;///与7无关的个数和
    LL sqsum;///平方和
}dp[20][20][20];///dp[i][j][k]表示处理的位数,数字和%7,数字%7
int digit[20];
LL p[20];///p[i] = 10^ i;
Num dfs(int pos , int pre1 , int pre2, bool fp)
{
    if(pos == -1)
    {
        Num tmp;
        tmp.cnt = (pre1 != 0) && (pre2 != 0);
        tmp.sum = tmp.sqsum = 0;
        return tmp;
    }
    if(!fp && dp[pos][pre1][pre2].cnt != -1)return dp[pos][pre1][pre2];
    int maxi = fp ? digit[pos] : 9;
    Num ans , tmp;
    ans.cnt = ans.sqsum = ans.sum = 0;
    for(int i = 0 ; i <= maxi ; i++)
    {
        if(i == 7)continue;
        tmp = dfs(pos - 1 , (pre1 + i) % 7 , (pre2 * 10 + i)% 7 , fp && i == maxi);
        ans.cnt += tmp.cnt;
        ans.cnt %= MOD;

        ans.sum += (tmp.sum + ((p[pos] * i) % MOD) * tmp.cnt % MOD) % MOD;
        ans.sum %= MOD;

        ans.sqsum += (tmp.sqsum + ((2 * p[pos] * i) % MOD) *tmp.sum) % MOD;///next^2 + 2*pre*10^pos*next
        ans.sqsum %= MOD;
        ans.sqsum += (tmp.cnt * p[pos]) % MOD * p[pos] % MOD * i * i % MOD;///(pre*10^pos)^2
        ans.sqsum %= MOD;
    }
    if(!fp)dp[pos][pre1][pre2] = ans;
    return ans;
}
LL f(LL n)
{
    int len = 0;
    while(n)
    {
        digit[len++] = n % 10;
        n /= 10;
    }
    return dfs(len - 1, 0 , 0 , 1).sqsum;
}
int main()
{
    int T;
    LL a , b;
    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 < 10 ; j++)
        {
            for(int k = 0 ; k < 10 ; k++)
            {
                dp[i][j][k].cnt = -1;
            }

        }
    }
    scanf("%d" , &T);
    while(T--)
    {
        scanf("%I64d%I64d" , &a , &b);
        LL ans = f(b) - f(a - 1);
        printf("%I64d\n" , (ans % MOD + MOD ) % MOD);
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值