[bzoj2326][HNOI2011]数学作业

本题是一个典型的使用矩阵优化的题。
本题的特殊之处在于:矩阵是会变化的,而不是固定的。
所以我们要分阶段搞,而不是一下子搞。
容易得出:
f[i] = f[i-1] * 10 ^ k + i;
其中k由i决定。
变形一下原式:
f[i] = f[i-1] * 10 ^ k + (i-1) + 1;
容易构建矩阵:

\[\begin{equation} \left[ \begin{array}{ccc} f[i] & i-1 & 1 \end{array} \right] \end{equation} \]

和变换矩阵:

\[\begin{equation} \left[ \begin{array}{ccc} 10^k & 0 & 0 \\ 1 & 1 & 0 \\ 1 & 1 & 1 \end{array} \right] \end{equation} \]

套用模板即可。
下面是代码。

#include <bits/stdc++.h>
using namespace std;
long long n, m;
#define ll long long
struct M {
    ll v[4][4];
    M() {
        memset(v, 0, sizeof(v));
    }
    friend void print(M a) {
        for(int i = 1; i <= 3; i++) {
            for(int j = 1; j <= 3; j++) 
                cout << a.v[i][j] << ' ';
            cout << endl;
        }
    }
    friend M operator * (M a, M b) {
        M ans;
        for(int i = 1; i <= 3; i++) {
            for(int j = 1; j <= 3; j++) {
                for(int k = 1; k <= 3; k++) {
                    ans.v[i][j] = (ans.v[i][j] % m + (a.v[i][k]%m * b.v[k][j] % m)%m) % m;
                }
            }
        }
       return ans;
    }
    friend M operator ^ (M a, ll b) {
        M ans;
        for(int i = 1; i <= 3; i++) ans.v[i][i] = 1;
        for(ll i = b; i; i >>= 1, a = a * a) {
            if(i & 1) ans = ans * a;
        }
        return ans;
    }
}a, b;
ll pow(ll a, ll b) {
    int ans = 1;
    for(int i = b; i; i >>= 1, a = a*a) 
        if(i &1) ans = ans*a;
    return ans;
}
int main() {
    scanf("%lld %lld", &n, &m);
    a.v[1][1]  = a.v[1][2] = 0;
    a.v[1][3] = 1;
    ll c = 10;
    ll d = 1;
    ll u = 0;
    b.v[1][1] = pow(10, d);
    b.v[2][1] =b.v[2][2]=b.v[3][1] = b.v[3][2]=b.v[3][3] = 1;
    while(c-1 < n) {
        a = a * (b ^ (pow(10, d) - u-1));
        u = pow(10, d)-1;
        d++;
        c*=10;
        b.v[1][1] = (b.v[1][1] * 10)%m;
    }
    b = b^(n-u);
    a = a*b;
    printf("%lld\n", a.v[1][1]);
    return 0;
}

注:代码在bzoj上可以ac,但是在洛谷上不保证ac。

posted on 2017-01-21 21:13  蒟蒻konjac  阅读(124)  评论(0编辑  收藏  举报