← 全部文章

CF575A Fibonotci 题解

本文目录(3 节)
  1. 题意
  2. 题解
  3. 代码

CF575A Fibonotci

题意

  • s0⋯+∞s_{0\dots +\infty} 是一个正整数序列。
  • 给定 s0…n−1s_{0 \dots n - 1},对于 i≥ni \ge n,有 mm 个 ii 给定 sis_i,剩下的 ii 满足 si=si mod ns_i = s_{i \bmod n}。
  • f0⋯+∞f_{0 \dots +\infty} 同样是一个正整数序列。
  • 给定 f0,f1f_0,f_1,对于 i≥2i \ge 2,fi=si−1fi−1+si−2fi−2f_i = s_{i-1}f_{i-1} + s_{i-2}f_{i-2}。
  • 求 fk mod pf_k \bmod p。
  • n,m≤5×104n,m \le 5 \times 10^4,k≤1018k \le 10^{18},si,p≤109s_i,p \le 10^9。

题解

首先考虑 m=0m = 0 的情况。

由于 fi=si−1fi−1+si−2fi−2f_i = s_{i-1}f_{i-1} + s_{i-2}f_{i-2},有:

[fi−2fi−1]×[0si−21si−1]=[fi−1fi] \begin{bmatrix} f_{i-2} & f_{i-1} \end{bmatrix} \times \begin{bmatrix} 0 & s_{i-2} \\\\ 1 & s_{i-1} \end{bmatrix} = \begin{bmatrix} f_{i-1} & f_i \end{bmatrix}

因此:

[f0f1]×∏i=1n[0si−11si mod n]=[fnfn+1] \begin{bmatrix} f_{0} & f_{1} \end{bmatrix} \times \prod_{i=1}^{n} \begin{bmatrix} 0 & s_{i-1} \\\\ 1 & s_{i \bmod n} \end{bmatrix} = \begin{bmatrix} f_{n} & f_{n+1} \end{bmatrix}

于是可以矩阵快速幂,最后一段可以暴力转移。

考虑 m≠0m \ne 0 时,线段树维护矩阵乘积。

每次只会修改两个矩阵,因此一共只有 O(m)\mathcal O(m) 个单点修改。

因此可以在没有特殊值的周期内用矩阵快速幂转移,有特殊值的周期内单点修改之后再转移再修改回去。

总时间复杂度 O(n+m(log⁡k+log⁡n))\mathcal O(n + m (\log k + \log n))。

代码

const int N = 5e4 + 7;
ll k, o;
int n, m, x;
modint s[N];

struct mt {
	int n, m; modint a[2][2];
	inline mt() {}
	inline mt(int n, int m) : n(n), m(m) { memset(a, 0, sizeof(a)); }
	inline friend mt operator * (mt x, mt y) {
		mt z(x.n, y.m);
		for (int i = 0; i < z.n; i++)
			for (int k = 0; k < x.m; k++)
				for (int j = 0; j < z.m; j++)
					z.a[i][j] += x.a[i][k] * y.a[k][j];
		return z;
	}
} a[N], b[N], f;

inline void ksm(mt x, ll y, mt &z) {
	while (y) {
		if (y & 1) z = z * x;
		x = x * x, y >>= 1;
	}
}

struct Q {
	ll x, k; modint y;
	inline bool operator < (Q o) { return x < o.x; }
} q[N<<1];

struct T {
	int l, r; mt x;
} t[N<<2];

inline void build(int p, int l, int r) {
	t[p].l = l, t[p].r = r;
	if (l == r) return t[p].x = a[l], void();
	build(ls, l, md), build(rs, md + 1, r);
	t[p].x = t[ls].x * t[rs].x;
}

inline void chg(int p, int x, mt y) {
	if (t[p].l == t[p].r) return t[p].x = y, void();
	chg(x <= md ? ls : rs, x, y), t[p].x = t[ls].x * t[rs].x;
}

int main() {
	rd(k), rd(P), rd(n);
	for (int i = 0; i < n; i++) rd(x), s[i] = x % P;
	for (int i = 1; i <= n; i++) a[i] = mt(2, 2), a[i].a[0][1] = s[i-1], a[i].a[1][1] = s[i%n], a[i].a[1][0] = 1, b[i] = a[i];
	build(1, 1, n), rd(m);
	for (int i = 1; i <= m; i++) rd(q[i].x), q[m+i].x = q[i].x + 1, q[i].k = 1, rd(x), q[m+i].y = q[i].y = x % P;
	sort(q + 1, q + (m <<= 1) + 1);
	while (m && q[m].x > k) --m;
	f = mt(1, 2), f.a[0][1] = 1;
	for (int i = 1, j = 1; i <= m; i = ++j) {
		ll w = (q[i].x - 1) / n;
		while (j < m && w == (q[j+1].x - 1) / n) ++j;
		ksm(t[1].x, w - o, f), o = w;
		for (int k = i; k <= j; k++) {
			int d = (q[k].x - 1) % n + 1;
			b[d].a[q[k].k][1] = q[k].y, chg(1, d, b[d]);
		}
		if (w == k / n) break;
		f = f * t[1].x, o = w + 1;
		for (int k = i; k <= j; k++) {
			int d = (q[k].x - 1) % n + 1;
			chg(1, d, b[d] = a[d]);
		}
	}
	ll w = k / n;
	ksm(t[1].x, w - o, f);
	for (int i = 1, d = k % n; i <= d; i++) f = f * b[i];
	print(f.a[0][0]);
	return 0;
}

标签:矩阵、线段树

评论

    填写昵称即可留言。评论经站主审核后公开,请勿提交私人信息。

    正在连接评论服务…