Hdu 5564 Clarke and digits

Problem Description
Clarke is a patient with multiple personality disorder. One day, Clarke turned into a researcher, did a research on digits.
He wants to know the number of positive integers which have a length in [l,r] and are divisible by 7 and the sum of any adjacent digits can not be k.

Input
The first line contains an integer T(1≤T≤5), the number of the test cases.
Each test case contains three integers l,r,k(1≤l≤r≤109,0≤k≤18).

Output
Each test case print a line with a number, the answer modulo 109+7.

Sample Input
2
1 2 5
2 3 5

Sample Output
13
125

Hint:
At the first sample there are 13 number 7 , 21 , 28 , 35 , 42 , 49 , 56 , 63 , 70 , 77 , 84 , 91 , 98 satisfied.

题意:

求长度在l到r区间内的数,相邻的两位数加起来不等于ban,总的整个数要能被7整除,这样的数有多少个。

思路:

话说一拿到这道题我首先想到的居然是去查能被7整除的性质,然后成功的发现了我是一个idiot。

然后开始正经的推DP公式,因为没有专项练习过数位DP,所以推得很吃力,不过总算是推出来了

f [ i ] [ j ] [ k ] = x = 0 9 f [ i 1 ] [ ( x 10 + k ) m o d 7 ] [ x ]

f[i][j][k] 表示长度为i的,能被7整除的,最后一位是k的方案数。

因为知道是矩阵快速幂,所以死命的往矩阵上面凑,然而这里除了i这一维可以快速幂搞掉的,还有两维,跟平常的不一样,于是我才思枯竭。。。

于是高端操作1出现了:f[i][j*10+k]表示的就是f[i][j][k],有点类似状态压缩,剪掉了一维,然后对于这个数组,就可以愉快地使用矩阵快速幂了。

然而因为求的是l到r的区间内的和,所以想到用前缀和一样的东西,最后相减。我最开始是把有前缀0的数也当做一个方案,然后顺利WA掉,于是转向常规做法题解

题解中高端操作2出现了:把求和写在矩阵乘法里面,在原来70*70的转移矩阵之外再开一行一列,而在答案矩阵中多开一列,答案矩阵最后那一列记录前面所有矩阵的所有mod 7 == 0的方案数的和(即前缀和)。

于是这道题解决了,以上是小越越做这道题的坎坷的心路历程。其实打起来只要思路清晰是非常快的,一些细节要在平常打题的时候就养成好习惯,这样就不会出现一些奇怪的根本不知道如何调试的错误了,然后打起来越快的方法越好,比如有一些初始化没必要写在结构体里面,有的时候重载运算符也是冗余的。

上代码,看起来还比较简洁吧:

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const ll MOD = 1e9+7;
const int N = 71;
struct MATRIX{
    ll a[N][N];
}a, b, ml, mr;
int T, l, r, ban;

MATRIX Mul(MATRIX x, MATRIX y)
{
    MATRIX z;
    memset(z.a, 0, sizeof(z.a));
    for (int i = 0; i < N; i++)
        for (int j = 0; j < N; j++)
            for (int k = 0; k < N; k++)
                z.a[i][j] = (z.a[i][j]+x.a[i][k]*y.a[k][j])%MOD;
    return z;
}

MATRIX Pow(MATRIX x, int y)
{
    MATRIX z;
    memset(z.a, 0, sizeof(z.a));
    for (int i = 0; i < N; i++)
        z.a[i][i] = 1;
    while (y > 0){
        if (y&1) z = Mul(z, x);
        x = Mul(x, x);
        y >>= 1;
    }
    return z;
}

int main()
{
    cin >> T;
    memset(a.a, 0, sizeof(a.a));
    for (int i = 1; i < 10; i++)
        a.a[0][i%7*10+i] = 1;
    while (T--){
        cin >> l >> r >> ban;
        memset(b.a, 0, sizeof(b.a));
        for (int i = 0; i < 7; i++)
            for (int j = 0; j < 10; j++)
                for (int k = 0; k < 10; k++)
                    if (j+k != ban)
                        b.a[i*10+j][(i*10+k)%7*10+k] = 1;
        for (int i = 0; i < 10; i++)
            b.a[i][N-1] = 1;
        b.a[N-1][N-1] = 1;
        ml = Mul(a, Pow(b, l-1));
        mr = Mul(a, Pow(b, r));
        cout << (mr.a[0][N-1]-ml.a[0][N-1]+MOD)%MOD << endl;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xyyxyyx/article/details/82597252