P3216 题解

· · 题解

题目传送门

构造出答案矩阵: $$ \begin{bmatrix} ans_{i-1} \\ i \\ 1 \\ \end{bmatrix} $$ 构造出转移矩阵: $$ \begin{bmatrix} 10^x&1&0 \\ 0&1&1 \\ 0&0&1 \\ \end{bmatrix} $$ 其中 $x$ 是这一步对应 $i$ 的数位数。 会发现 $x$ 会随着 $i$ 改变,因此不能一次快速幂解决,需要按数位进行分类,对于每一类单独构建转移矩阵,快速幂之后和之前的结果相乘,快速幂的幂次是 $\min(n-10^{x-1}+1,10^x-10^{x-1})$,也就是这一层进行转移的次数(需要注意,由于初始状态不需要进行转移,一位数的时候需要额外减 $1$)。 #### 注意!顺序很重要,需要先快速幂再去乘原来的答案! 代码: ```cpp #include<iostream> #include<cstring> using namespace std; unsigned long long n,m,p10[20]; struct matrix { int a[105][105] = {}; matrix() {memset(a,0,sizeof(a));} matrix operator * (const matrix b) { matrix c; for(int i = 1;i <= 3;i++) for(int j = 1;j <= 3;j++) for(int k = 1;k <= 3;k++) { c.a[i][j] += 1ll * a[i][k] * b.a[k][j] % m; c.a[i][j] %= m; } return c; } void output() { for(int i = 1;i <= 3;i++) { for(int j = 1;j <= 3;j++) printf("%lld ",a[i][j]); printf("\n"); } } }; matrix pow(matrix x,long long b) { matrix ans; for(int i = 1;i <= 3;i++) ans.a[i][i] = 1; while(b) { if(b&1) ans = ans * x; x = x * x; b >>= 1; } return ans; } int digits(long long x) { int cnt = 0; while(x) { cnt++; x /= 10; } return cnt; } int main() { p10[0] = 1; for(int i = 1;i < 20;i++) p10[i] = p10[i-1] * 10ll; scanf("%lld%lld",&n,&m); matrix p,ans; for(int i = 1;i <= 3;i++) ans.a[i][i] = 1; for(int i = 1;i <= digits(n);i++) { p.a[1][2] = p.a[2][2] = p.a[2][3] = p.a[3][3] = 1; p.a[1][1] = p10[i] % m; if(i == 1) ans = ans * pow(p,min(n-p10[i-1],p10[i]-p10[i-1]-1)); else ans = pow(p,min(n-p10[i-1]+1,p10[i]-p10[i-1])) * ans; } printf("%lld",(ans.a[1][1] + 2ll * ans.a[1][2] + ans.a[1][3]) % m); return 0; } ```