acwing 1086 恨7不成妻

题面

在这里插入图片描述

题解

在这里插入图片描述

代码

#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;
typedef long long ll;
const int N = 20, P = 1e9 + 7;

struct F {
    int s0;  //个数(0次方)
    int s1;  //和(1次方)
    int s2;  //平方和(2次方)
} f[N][10][7][7];   //i位数,最高位j,自身模7的值,各位数之和模7的值

int power7[N];   //10的i次方模7的值
int power9[N];   //10的i次方模P的值

//返回模y的正数
int mod(ll x, int y) {
    return (x % y + y) % y;
}

void init() {
    //预处理一位
    for (int i = 0; i <= 9; i++) {
        if (i == 7) continue;
        auto &v = f[1][i][i % 7][i % 7];
        v.s0++, v.s1 += i, v.s2 += i * i;
    }

    ll power = 10;
    for (int i = 2; i < N; i++, power *= 10) {//位数
        for (int j = 0; j <= 9; j++) { //最高位
            if (j == 7) continue;
            for (int a = 0; a < 7; a++) {//自身模7
                for (int b = 0; b < 7; b++) {//位数之和模7
                    auto &v1 = f[i][j][a][b];
                    for (int k = 0; k <= 9; k++) { //次高位
                        if (k == 7) continue;
                        auto &v2 = f[i - 1][k][mod(a - j * power, 7)][mod(b - j, 7)];
                        v1.s0 = mod(v1.s0 + v2.s0, P);
                        v1.s1 = mod(v1.s1 + v2.s1 + j * (power % P) % P * v2.s0, P);
                        v1.s2 = mod(v1.s2 +
                                    j * j * (power % P) % P * (power % P) % P * v2.s0 +
                                    v2.s2 +
                                    2 * j * power % P * v2.s1, P);
                    }
                }
            }
        }
    }


    power7[0] = 1;
    for (int i = 1; i < N; i++) power7[i] = power7[i - 1] * 10 % 7;

    power9[0] = 1;
    for (int i = 1; i < N; i++) power9[i] = power9[i - 1] * 10ll % P;
}

F get(int i, int j, int a, int b) {
    int s0 = 0, s1 = 0, s2 = 0;
    for (int x = 0; x < 7; x++)
        for (int y = 0; y < 7; y++)
            if (x != a && y != b) {  //不是a,b的就是合法状态
                auto v = f[i][j][x][y];
                s0 = (s0 + v.s0) % P;
                s1 = (s1 + v.s1) % P;
                s2 = (s2 + v.s2) % P;
            }
    return {s0, s1, s2};
}

int dp(ll n) {
    if (!n) return 0;

    ll backup_n = n % P;   //备份n
    vector<int> nums;
    while (n) nums.push_back(n % 10), n /= 10;

    int res = 0;
    ll last_a = 0;  //前i位的值
    ll last_b = 0;  //前i位的位数和和
    for (int i = nums.size() - 1; i >= 0; i--) {
        int x = nums[i];
        for (int j = 0; j < x; j++) { //左分支
            if (j == 7) continue;
            //先列出a,b的不合法状态
            int a = mod(-last_a * power7[i + 1], 7);
            int b = mod(-last_b, 7);
            auto v = get(i + 1, j, a, b);  //get得到合法状态
            //套公式
            res = mod(res +
                    (last_a % P) * (last_a % P) % P * power9[i + 1] % P * power9[i + 1] % P * v.s0 % P +
                    v.s2 +
                    2 * last_a % P * power9[i + 1] % P * v.s1,P);
        }

        if (x == 7) break;  //右分支更新
        last_a = last_a * 10 + x;
        last_b += x;
        //特判
        if (!i && last_a % 7 && last_b % 7) res = (res + backup_n * backup_n) % P;
    }

    return res;
}

int main() {
    int T;
    cin >> T;

    init();

    while (T--) {
        ll l, r;
        cin >> l >> r;
        cout << mod(dp(r) - dp(l - 1), P) << endl;
    }

    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值