初見安~這裏是傳送門:洛谷P3216 數學作業
題解
題意很簡單,求n個數依次寫過去得到的這個數模m的值。
我們很容易得到一個線性的遞推式是: ,其中w是i的位數。但是題目的話明顯過不了。
線性遞推式+線性過不了,我們就可以想到矩陣快速冪了。
f的值需要計算,i的值需要累加,所以我們的矩陣裏面需要放三個東西:。轉移就是:
但是轉移矩陣中的10^w會因爲數位的改變而改變 所以我們還得枚舉n的數位改變轉移矩陣來進行快速冪。
手枚可得:
時,有9個數w=1;
時,有90個數w=2;
時,有900個數w=3;
……
時,有個數w=x。
所以就可以寫代碼了。
當然,上文3*1的矩陣放到3*3的矩陣中會方便很多,可以直接在每一行後面補兩位0。
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
ll read() {
ll x = 0, f = 1, ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x * f;
}
ll n, m;
struct node {
ll x[3][3];
}tran, init, tmp;
node mul(node a, node b) {//矩陣乘法
node c; memset(c.x, 0, sizeof c.x);
for(int i = 0; i < 3; i++) for(int j = 0; j < 3; j++) for(int k = 0; k < 3; k++) c.x[i][j] = (c.x[i][j] + a.x[i][k] * b.x[k][j] % m) % m;
return c;
}
node pw(node a, ll b) {//快速冪
node ans; memset(ans.x, 0, sizeof ans.x); ans.x[0][0] = ans.x[1][1] = ans.x[2][2] = 1;
while(b) {if(b & 1) ans = mul(a, ans); a = mul(a, a), b >>= 1;}
return ans;
}
signed main() {
n = read(), m = read();
tran.x[0][1] = tran.x[0][2] = tran.x[1][1] = tran.x[1][2] = tran.x[2][2] = 1;
init.x[2][0] = 1;//tran是轉移矩陣 先初始化;init是目標矩陣
for(ll i = 1; i <= n; i *= 10) {
memcpy(tmp.x, tran.x, sizeof tmp.x); tmp.x[0][0] = i * 10 % m;//tmp是當前轉移矩陣
node ttp = pw(tmp, min(i * 10 - i, n - i + 1));//次數注意上界
init = mul(ttp, init);//init是目標矩陣
}
printf("%lld\n", init.x[0][0]);
return 0;
}
迎評:)
——End——