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=sn1Fn1+sn2Fn2
  • 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 jN(即特殊情况不会第一段循环节内),
  • F k m o d    P F_k\mod P FkmodP.
  • N , M ≤ 5 ∗ 1 0 4 N,M≤5*10^4 N,M5104
  • P , s i , v ≤ 1 0 9 P,s_i,v≤10^9 P,si,v109
  • k , j ≤ 1 0 18 k,j≤10^{18} k,j1018

题解

  • 如果没有特殊限制,看到 k k k那么大,直接就会想到矩阵乘法,
  • 由于每一位的系数不一样,可以先用倍增求出不同位下( < N <N <N)向右 2 i 2^i 2i步的转移矩阵( 2 ∗ 2 2*2 22),然后直接倍增做即可。
  • 但是有特殊限制的话,该怎么办呢?
  • 其实方法也是类似的,只是矩乘不是一次做到底,而是每次碰到一个限制就停下来,然后对于特殊限制,直接往后计算两位,因为被修改的值在计算 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;
}
posted @ 2020-09-23 21:27  AnAn_119  阅读(64)  评论(0编辑  收藏  举报