HDU 4507 吉哥系列故事——恨7不成妻(数位DP,细节好多)

贴几篇讲的比较好的博客
https://blog.csdn.net/qq_34374664/article/details/53128417
https://www.cnblogs.com/kuangbin/archive/2013/05/01/3053233.html

//http://acm.hdu.edu.cn/discuss/problem/post/reply.php?postid=31637&messageid=1&deep=0
/*
 *  如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
  1、整数中某一位是7;
  2、整数的每一位加起来的和是7的整数倍;
  3、这个整数是7的整数倍;

求一个区间中与7无关的数的平方和

*/
#include <iostream>
#include  <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const ll MOD = 1e9 + 7;
struct node {
    ll cnt, sum, sqsum;
    node() {
        cnt = 0;
        sum = 0;
        sqsum = 0;
    }
} dp[20][20][20]; //分别是处理的数位、数字和%7,数%7
int cnt, a[100];
ll p[20];
node dfs(int pos,  int mod, int sum, int limit) {
    node ans;
    if(pos == -1)  {
        node tmp;
        tmp.cnt = (mod != 0 && sum != 0);
        tmp.sqsum = tmp.sum = 0;
        return tmp;
    }
    if(!limit && dp[pos][mod][sum].cnt != -1) return dp[pos][mod][sum];
    int up = limit ? a[pos] : 9;
    for(int i = 0; i <= up; i++) {
        if(i == 7)continue;
        node tmp;
        tmp = dfs(pos - 1, (mod + i) % 7, (sum * 10 + i) % 7, limit && i == up);
        tmp = dfs(pos - 1, (mod + i) % 7, (sum * 10 + i) % 7, limit && i == up);
        ans.cnt += tmp.cnt;
        ans.cnt %= MOD;
        ans.sum += (tmp.sum + ((p[pos] * i) % MOD) * tmp.cnt % MOD ) % MOD;
        /*
        这边和的时候乘上cnt是因为假如说有合法状态234 235 236,那么你求所有数相加的时候会加上3个200
        其中3就是tmp.cnt ,代表了上一层所有的合法情况种数
        需要注意的是,tmp.sum表示的是所有上一层次的和了
        */
        ans.sum %= MOD;
        //  !!!这边由于数据量巨大,很容易就爆了long long ,因此需要注意时取模
        ans.sqsum += (tmp.sqsum + ( (2 * p[pos] * i) % MOD ) * tmp.sum) % MOD;
        ans.sqsum %= MOD;
        ans.sqsum += ( (tmp.cnt * p[pos]) % MOD * p[pos] % MOD * i * i % MOD );
        /*
        至于这边平方和为什么是这么相加,可以参考这段话

        题解:关于数字7的限制其实不难实现,难的是要求平方和,考虑2XX这个数,也就是百位为2的数,它满足限制条件的平方和是多少?
        假设满足条件的数有234,245,266,那么 234^2 + 245^2 + 266^2 = (200 + 34)^2 + (200 + 45)^2 + (200 + 66)^2
        = 3*200^2 + 2*200*(34+45+66) + (34^2 + 35^2 + 66^2),
        因此在枚举到2的时候,表达式里只有3,(34 + 45 + 66),(34^2 + 35^2 + 66^2)不知道,
        因此我们可以定义dfs1(i)为求平方和,dfs2(i)为求和,dfs3(i)为求有多少个满足条件的数(也就是上述的3)。
        */
        ans.sqsum %= MOD;
    }
    if(!limit) dp[pos][mod][sum] = ans;
    return ans;
}

ll solve(ll x) {
    cnt = 0;
    while(x) {
        a[cnt++] = x % 10;
        x /= 10;
    }
    return dfs(cnt - 1, 0, 0, true).sqsum;
}

int main() {
    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;
    ll n, m;
    p[0] = 1;
    for(int i = 1; i <= 19; i++) {
        p[i] = p[i - 1] * 10 % MOD;
    }
    int t;
    scanf("%d", &t);
    while(t--) {
        scanf("%I64d%I64d", &n, &m);
        ll ans = solve(m) % MOD;
        ans -= solve(n - 1);
        ans = (ans + MOD) % MOD;
        printf("%I64d\n", ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yiqzq/article/details/81125194