Codeforces 575A. Fibonotci(矩阵乘法)
Codeforces 575A. Fibonotci
题目大意
- 给出一个递推式: F n = s n − 1 ∗ F n − 1 + s n − 2 ∗ F n − 2 F_n=s_{n-1}*F_{n-1}+s_{n-2}*F_{n-2} Fn=sn−1∗Fn−1+sn−2∗Fn−2,
- 且 F 0 = 0 F_0=0 F0=0, F 1 = 1 F_1=1 F1=1.
- s s s的值有周期性,除特殊限制外 s i = s i m o d N s_i=s_{i\mod N} si=simodN,
- 特殊限制有 m m m组,每组的形式为将 s j s_j sj改为 v v v,其中 j ≥ N j≥N j≥N(即特殊情况不会第一段循环节内),
- 求 F k m o d P F_k\mod P FkmodP.
- N , M ≤ 5 ∗ 1 0 4 N,M≤5*10^4 N,M≤5∗104
- P , s i , v ≤ 1 0 9 P,s_i,v≤10^9 P,si,v≤109
- k , j ≤ 1 0 18 k,j≤10^{18} k,j≤1018
题解
- 如果没有特殊限制,看到 k k k那么大,直接就会想到矩阵乘法,
- 由于每一位的系数不一样,可以先用倍增求出不同位下( < N <N <N)向右 2 i 2^i 2i步的转移矩阵( 2 ∗ 2 2*2 2∗2),然后直接倍增做即可。
- 但是有特殊限制的话,该怎么办呢?
- 其实方法也是类似的,只是矩乘不是一次做到底,而是每次碰到一个限制就停下来,然后对于特殊限制,直接往后计算两位,因为被修改的值在计算 s n + 1 s_{n+1} sn+1和 s n + 2 s_{n+2} sn+2时都需要,
- 当然会有一些要注意的地方,比如出现连续的限制,那么中间一次矩乘都不能做,而是要把一连串的限制都计算完后,再继续做矩乘。
- 预处理复杂度 O ( N log 2 k ) O(N\log_2k) O(Nlog2k),矩乘总复杂度 O ( N log 2 k ) O(N\log_2k) O(Nlog2k)。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 50010
ll a[N], f[N][62][2][2];
struct node {
ll x, s;
}c[N];
int cmp(node x, node y) {
return x.x < y.x;
}
int main() {
ll K, P, i, j;
int n, m;
scanf("%lld%lld", &K, &P);
scanf("%d", &n);
for(i = 0; i < n; i++) scanf("%lld", &a[i]);
scanf("%d", &m);
for(i = 1; i <= m; i++) scanf("%lld%lld", &c[i].x, &c[i].s);
m++;
c[m].x = K + 1, c[m].s = 0;
sort(c + 1, c + m + 1, cmp);
for(i = 0; i < n; i++) {
f[i][0][0][0] = 0;
f[i][0][1][0] = 1;
f[i][0][0][1] = a[(i - 1 + n) % n];;
f[i][0][1][1] = a[i];
}
for(j = 1; j < 62; j++) {
for(i = 0; i < n; i++) {
ll k = (i + (1ll << (j - 1))) % n;
f[i][j][0][0] = (f[i][j - 1][0][1] * f[k][j - 1][1][0] + f[i][j - 1][0][0] * f[k][j - 1][0][0]) % P;
f[i][j][0][1] = (f[i][j - 1][0][1] * f[k][j - 1][1][1] + f[i][j - 1][0][0] * f[k][j - 1][0][1]) % P;
f[i][j][1][0] = (f[i][j - 1][1][1] * f[k][j - 1][1][0] + f[i][j - 1][1][0] * f[k][j - 1][0][0]) % P;
f[i][j][1][1] = (f[i][j - 1][1][1] * f[k][j - 1][1][1] + f[i][j - 1][1][0] * f[k][j - 1][0][1]) % P;
}
}
ll la = 1;
ll s0 = 0, s1 = 1;
for(i = 1; i <= m; i++) {
ll t = c[i].x, p = t - la, x = la;
ll g[2][2], h[2][2];
g[0][0] = g[1][1] = 1, g[0][1] = g[1][0] = 0;
for(j = 61; j >= 0; j--) if(p >= (1ll << j)) {
p -= 1ll << j;
h[0][0] = (g[0][1] * f[x % n][j][1][0] + g[0][0] * f[x % n][j][0][0]) % P;
h[0][1] = (g[0][1] * f[x % n][j][1][1] + g[0][0] * f[x % n][j][0][1]) % P;
h[1][0] = (g[1][1] * f[x % n][j][1][0] + g[1][0] * f[x % n][j][0][0]) % P;
h[1][1] = (g[1][1] * f[x % n][j][1][1] + g[1][0] * f[x % n][j][0][1]) % P;
g[0][0] = h[0][0], g[1][0] = h[1][0], g[0][1] = h[0][1], g[1][1] = h[1][1];
x += 1ll << j;
}
ll t0 = (s0 * g[0][0] + s1 * g[1][0]) % P, t1 = (s0 * g[0][1] + s1 * g[1][1]) % P;
if(t == K + 1) {
printf("%lld\n", t0 % P);
break;
}
s1 = (t0 * a[(t - 1 + n) % n] + t1 * c[i].s) % P;
s0 = t1 % P;
la = t + 1;
while(c[i + 1].x == c[i].x + 1) {
ll s2 = (s0 * c[i].s + s1 * c[i + 1].s) % P;
s0 = s1, s1 = s2;
la++;
if(la == K) {
printf("%lld\n", s1);
return 0;
}
i++;
}
ll s2 = (s0 * c[i].s + s1 * a[la % n]) % P;
s0 = s1, s1 = s2;
la++;
if(la == K) {
printf("%lld\n", s1);
break;
}
}
return 0;
}
哈哈哈哈哈哈哈哈哈哈