HDU-4507

HDU-4507

题面

单身!
  依然单身!
  吉哥依然单身!
  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

思路

数位DP,我通过这篇博客学习

与博客中普通的数位DP计数思路不同,本题需要求解所有数的平方和。

由数位DP上一位推往这一位的列公式可得,上一位(x)这一位(y):

(10*x+y)^2 = 100*x^2 + y^2 + 20*x*y ……公式1

所以转移方程就变为:

ans.cnt = ans.cnt + temp.cnt

有cnt个数满足第pos位是i,所以有:

ans.sum = temp.sum + i*len[pos]*temp.cnt

所以由公式1可以得到:

ans.sum2+=i*len[pos]*i*len[pos]*temp.cnt+temp.sqsum+2*i*len[pos]*temp.sum

记得每次乘后要对题目要求的mod取余,否则数字过大会超过LL的范围

记录sign1:所有位数和,sign2:该数

存在dp数组中,方便下次搜索到该位且拥有相同的sign1和sign2时,可以直接返回结果。

代码

#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const ll mod = 1e9 + 7;
int num[30];
ll len[30];
struct node {
    ll cnt;
    ll sum;   // 存数位累加和
    ll sqsum; // 存数位平方累加和
    node(int c=0, int s=0, int sq=0) : cnt(c), sum(s), sqsum(sq) {;}
} dp[20][10][10];
node dfs(int pos,int sign1, int sign2, bool limit) {
    // sign1 记录各个位和
    // sign2 记录该数和
    if(pos == -1) {
        node temp(0, 0, 0);
        if(sign1 && sign2) {
            temp.cnt = 1;
        }
        return temp;
    }
    if(!limit && dp[pos][sign1][sign2].cnt != -1) {
        return dp[pos][sign1][sign2];
    }
    int up = limit ? num[pos] : 9;
    node ans(0, 0, 0);
    for(int i=0;i<=up;i++) {
        if(i == 7) {
            continue;
        }
        node temp = dfs(pos-1, (sign1+i)%7, (sign2*10+i)%7, limit && num[pos] == i);
        ans.cnt = (ans.cnt + temp.cnt) % mod;
        // ans.sum = temp.sum + i*len[pos]*temp.cnt
        ans.sum=(ans.sum+(i*len[pos]%mod*temp.cnt%mod+temp.sum)%mod)%mod;
        // ans.sum2+=i*len[pos]*i*len[pos]*temp.cnt+temp.sqsum+2*i*len[pos]*temp.sum
        ans.sqsum=(ans.sqsum+(i*len[pos]%mod*i%mod*len[pos]%mod*temp.cnt%mod+temp.sqsum)%mod+2*i%mod*len[pos]%mod*temp.sum%mod)%mod;
    }
    if(!limit) {
        dp[pos][sign1][sign2] = ans;
    }
    return ans;
}
ll solve(ll x) { // 分解出各个位
    memset(dp, -1, sizeof(dp));
    int sign = 0;
    while(x) {
        num[sign++] = x % 10;
        x/=10; 
    }
    return dfs(sign-1, 0, 0, true).sqsum % mod;
}
int main() {
    len[0] = 1;
    for(int i=1;i<20;i++) {
        len[i] = len[i-1] * 10 % mod;
    }
    int n;
    cin >> n;
    while(n--) {
        ll l, r;
        cin >> l >> r;
        cout << ((solve(r) - solve(l-1)) % mod + mod) % mod << endl;
    }
}
posted @ 2020-09-02 19:52  骇人的籽  阅读(121)  评论(0编辑  收藏  举报