【题解】【CodeForces575A】Fibonotci

这是一个比较水但是需要一定码力才能码出来的题。

题目链接

点击打开链接

题目解法

考虑到这是个递推式,因此采用矩阵优化地推。那么构造以下算式:

\[\begin{pmatrix}f_{0}&f_{1}\end{pmatrix}\cdot \begin{pmatrix}1&s_0\\0&s_1\\\end{pmatrix}\cdots\begin{pmatrix}1&s_n\\0&s_{n+1}\end{pmatrix}=\begin{pmatrix}f_{n+1}& f_{n+2}\end{pmatrix} \]

所以我们只需要求一大堆矩阵的乘积。

由于这是循环的,所以很多地方可以直接当成一个循环节的矩阵积的若干次方。有时还要乘上前缀积与后缀积。不过这样你会发现你还需要乘上区间积:如果两个修改在同一个区间内,那么这两个修改之间的矩阵乘积还需要计算一下,但是你发现使用前缀积计算是不成立的,因为不一定每个前缀积都可以求逆。

所以你需要一个线段树维护区间乘积。时间复杂度 \(\mathcal O(m(\log k+\log n))\) 。不过这种方法我没有写过,而且也没看到任何题解这么写的,因此我怀疑有一些我没考虑到的问题,各位可以尝试这样写一下。

不过还有另外一种方法(这种方法可能有一大堆细节问题需要考虑)

不带修改的周期可以直接快速幂。考虑用线段树维护有修改的周期,每次在线段树上单点修改一个矩阵,然后再求整体乘积。

下面代码是这种方法。

代码

#include 
#include 
#include 
using namespace std;
typedef long long ll;
const ll NM = 5e4 + 5;
typedef long long ll;
ll k, mod, n, m;
ll ml(ll x, ll y) { return 1ll * x * y % mod; }
ll ad(ll x, ll y) { return (x + y > mod) ? (x + y - mod) : (x + y); }
ll dc(ll x, ll y) { return (x - y < 0) ? (x - y + mod) : (x - y); }
struct Mat {
	ll A[2][2];
	Mat() {}
	ll * operator [] (ll i) { return A[i]; }
	const ll * operator [] (ll i) const { return A[i]; }
	void Init() {
		A[0][0] = 1;
		A[0][1] = 0;
		A[1][0] = 0;
		A[1][1] = 1;
	}
	void Clear() { memset(A, 0, sizeof(A)); }
	void print() {
		for (ll i = 0; i < 2; ++i) {
			printf("	");
			for (ll j = 0; j < 2; ++j)
				printf("%lld ", A[i][j]);
			printf("\n");
		}
	}
};
struct LYK {
	ll i, x, y, v;
	LYK() {}
	LYK(ll a, ll b, ll c, ll d) : i(a), x(b), y(c), v(d) {}
} szj[NM << 1];
struct SZJ {
	ll i;
	Mat A;
} chg[NM << 1];
bool cmp(const LYK &x, const LYK &y) { return x.i < y.i; }
Mat operator * (const Mat &x, const Mat &y) {
	Mat ret;
	ret.Clear();
	for (ll i = 0; i < 2; ++i)
		for (ll j = 0; j < 2; ++j)
			for (ll k = 0; k < 2; ++k)
				ret[i][j] = ad(ret[i][j], ml(x[i][k], y[k][j]));
	return ret;
}
ll s[NM], ct, mt;
ll blk[NM << 1];
Mat ltg[NM], bas;
void ksm(Mat &x, Mat y, ll c) {
	if (!c) return ;
	for (; c; c >>= 1, y = y * y)
		if (c & 1) x = x * y;
}
Mat tr[NM << 2];
void Upd(ll o) { tr[o] = tr[o << 1] * tr[o << 1 | 1]; }
void Build(ll o, ll sl, ll sr) {
	if (sl == sr) {
		tr[o] = ltg[sl];
		return ;
	}
	ll mid = (sl + sr) >> 1;
	Build(o << 1, sl, mid);
	Build(o << 1 | 1, mid + 1, sr);
	Upd(o);
}
void Modify(ll o, ll sl, ll sr, ll ps, Mat v) {
	if (sl == sr) {
		tr[o] = v;
		return ;
	}
	ll m = (sl + sr) >> 1;
	if (ps <= m) Modify(o << 1, sl, m, ps, v);
	else Modify(o << 1 | 1, m + 1, sr, ps, v);
	Upd(o);
}
int main() {
	scanf("%lld%lld", &k, &mod);
	if (k == 0) printf("0\n");
	if (k == 1) printf("%d\n", mod != 1);
	if (k == 0 || k == 1) return 0;
	--k;
	scanf("%lld", &n);
	for (ll i = 0; i < n; ++i) {
		scanf("%lld", &s[i]);
		s[i] = s[i] % mod;
	}
	for (ll i = 0; i < n; ++i) {
		ltg[i][0][0] = 0;
		ltg[i][0][1] = s[i];
		ltg[i][1][0] = 1;
		ltg[i][1][1] = s[(i == n - 1) ? 0 : (i + 1)];
	}
	bas.Init(); //base的初始化 
	for (ll i = 0; i < n; ++i)
		bas = bas * ltg[i];
	scanf("%lld", &m);
	for (ll i = 1; i <= m; ++i) {
		ll j, v;
		scanf("%lld%lld", &j, &v);
		szj[++ct] = LYK(j, 0, 1, v);
		szj[++ct] = LYK(j - 1, 1, 1, v);
	}
	sort(szj + 1, szj + ct + 1, cmp);
	for (ll i = 1; i <= ct; ++i) {
		if (i != 1 && szj[i].i == szj[i - 1].i)
			chg[mt].A[szj[i].x][szj[i].y] = szj[i].v;
		else {
			chg[++mt].A = ltg[szj[i].i % n];
			chg[mt].A[szj[i].x][szj[i].y] = szj[i].v;
			chg[mt].i = szj[i].i;
		}
	}
	for (ll i = 1; i <= mt; ++i) blk[i] = chg[i].i / n;
	Mat ans;
	ans.Clear();
	ans[0][0] = 0;
	ans[0][1] = 1;
	ll ed = -1;
	Build(1, 0, n - 1);
	ll i, lst = -1, finb = k / n;
	for (i = 1; i <= mt; ) {
		if (blk[i] >= finb)
			break ;
		ksm(ans, bas, blk[i] - lst - 1);
		ll j = i;
		for (; blk[i] == blk[j]; ++i)
			Modify(1, 0, n - 1, chg[i].i % n, chg[i].A);
		ans = ans * tr[1];
		for (ll tt = j; tt < i; ++tt)
			Modify(1, 0, n - 1, chg[tt].i % n, ltg[chg[tt].i % n]);
		lst = blk[i - 1];
		ed = (lst + 1) * n - 1;
	}
	ll nbblk = (k - ed) / n;
	if (k % n == n - 1)
		--nbblk;
	ksm(ans, bas, nbblk);
	ed = ed + nbblk * n;
	ll res = k - ed;
	ll pt = i;
	for (i = 1; i <= res; ++i) {
		if (pt <= mt && chg[pt].i == ed + i)
			ans = ans * chg[pt++].A;
		else ans = ans * ltg[i - 1];
	}
	printf("%lld\n", ans[0][0]);
	return 0;
}

总结

  • 嗯...主要在调代码吧。写之前要先想好边界如何处理。

你可能感兴趣的:(【题解】【CodeForces575A】Fibonotci)