CF575A Fibonotci
这个题目兴许可以当做动态 D p \tt Dp Dp 入门?
考虑如果直接给定了 s s s 没有修改的话,我们直接对于这些 s s s 矩阵乘起来直接做即可。
考虑题目一个比较简单的形式,就是如果每一段只有一个位置被修改,那么我们直接维护前缀积,后缀积即可。
那么我们推广一下这个本质上就是区间查询单点修改的题目,因为矩阵的逆矩阵不一定存在,我们只能使用线段树进行维护。
我们对于每一段进行单独考虑,修改里面的所有值,之后再改回来即可。
我也不知道为什么我会在这题上耗一个上午。
具体实现方面我们考虑线段树区间变成
0
∼
n
−
1
0 \sim n - 1
0∼n−1 对于位置
i
i
i 其维护的矩阵是:
[
0
s
i
1
s
i
+
1
]
\left[ \begin{matrix} 0 & s_i\\ 1 & s_{i + 1} \end{matrix} \right]
[01sisi+1]
我们每次修改需要改两个值,为了方便我们将其拆开,这样可以不需要在进行修改的时候进行分类讨论当前位置可能属于两个区间的情况。
#include <bits/stdc++.h>
using namespace std;
//#define Fread
//#define Getmod
#ifdef Fread
char buf[1 << 21], *iS, *iT;
#define gc() (iS == iT ? (iT = (iS = buf) + fread (buf, 1, 1 << 21, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
#define getchar gc
#endif // Fread
template <typename T>
void r1(T &x) {
x = 0;
char c(getchar());
int f(1);
for(; c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(; '0' <= c && c <= '9';c = getchar()) x = (x * 10) + (c ^ 48);
x *= f;
}
template <typename T,typename... Args> inline void r1(T& t, Args&... args) {
r1(t); r1(args...);
}
#define int long long
const int maxn = 2e5 + 5;
const int maxm = maxn << 1;
typedef long long ll;
ll K;
int n, m, mod;
struct Matrix {
int a[2][2];
Matrix(void) { memset(a, 0, sizeof(a)); }
void init() {
memset(a, 0, sizeof(a));
a[0][0] = a[1][1] = 1;
}
Matrix operator * (const Matrix &z) const {
Matrix res;
for(int i = 0; i < 2; ++ i)
for(int j = 0; j < 2; ++ j) {
for(int k = 0; k < 2; ++ k) {
res.a[i][j] = (res.a[i][j] + 1ll * a[i][k] * z.a[k][j] % mod) % mod;
}
}
return res;
}
};
Matrix t[maxn << 2], nul;
int s[maxn];
struct Seg {
#define ls (p << 1)
#define rs (p << 1 | 1)
#define mid ((l + r) >> 1)
void pushup(int p) {
t[p] = t[ls] * t[rs];
}
void build(int p,int l,int r) {
if(l == r) {
t[p].a[0][0] = 0;
t[p].a[1][0] = 1;
t[p].a[0][1] = s[l];
t[p].a[1][1] = s[(l + 1) % n];
return ;
}
build(ls, l, mid), build(rs, mid + 1, r);
pushup(p);
}
void change(int p,int l,int r,int pos, int ps,int c) {
if(l == r) {
t[p].a[ps][1] = c;
return ;
}
if(pos <= mid) change(ls, l, mid, pos, ps, c);
else change(rs, mid + 1, r, pos, ps, c);
pushup(p);
}
Matrix Ask(int p,int l,int r,int ll,int rr) {
if(ll > rr) return nul;
if(ll <= l && r <= rr) return t[p];
Matrix res; res.init();
if(ll <= mid) res = res * Ask(ls, l, mid, ll, rr);
if(mid < rr) res = res * Ask(rs, mid + 1, r, ll, rr);
return res;
}
#undef ls
#undef rs
#undef mid
}T;
struct Node {
int opt;
long long x;
int val;
int operator < (const Node &z) const {
return x == z.x ? opt < z.opt : x < z.x;
}
};
vector<Node> vc;
Matrix ksm(Matrix tmp, int mi) {
Matrix res;
res.init();
while(mi) {
if(mi & 1) res = res * tmp;
mi >>= 1;
tmp = tmp * tmp;
}
return res;
}
signed main() {
// freopen("S.in", "r", stdin);
// freopen("S.out", "w", stdout);
int i, j;
nul.init();
r1(K, mod, n);
for(i = 0; i < n; ++ i) r1(s[i]), s[i] %= mod;
r1(m);
for(i = 1; i <= m; ++ i) {
ll a;
int b;
r1(a, b); b %= mod;
vc.push_back((Node) { 0, a, b });
if(a) vc.push_back((Node) { 1, a - 1, b });
}
Matrix res; res.init();
vc.push_back((Node) { 0, -n, 0 } );
vc.push_back((Node) { 0, (ll)2e18, 0 } );
sort(vc.begin(), vc.end());
T.build(1, 0, n - 1);
int las(0), ls;
for(i = 1; i < vc.size(); i = ls + 1) {
ls = i;
while(ls + 1 < vc.size() && vc[ls + 1].x / n == vc[i].x / n) ++ ls;
if(vc[i].x / n >= K / n) { las = vc[i - 1].x / n; break; }
res = res * ksm(T.Ask(1, 0, n - 1, 0, n - 1), vc[i].x / n - vc[i - 1].x / n - 1);
for(j = i; j <= ls; ++ j) T.change(1, 0, n - 1, vc[j].x % n, vc[j].opt, vc[j].val);
res = res * T.Ask(1, 0, n - 1, 0, n - 1);
for(j = i; j <= ls; ++ j) T.change(1, 0, n - 1, vc[j].x % n, vc[j].opt, !vc[j].opt ? s[vc[j].x % n] : s[(vc[j].x + 1) % n]);
}
res = res * ksm(T.Ask(1, 0, n - 1, 0, n - 1), K / n - 1 - las);
for(i = 0; i < vc.size(); ++ i) if(K / n == vc[i].x / n) T.change(1, 0, n - 1, vc[i].x, vc[i].opt, vc[i].val);
res = res * T.Ask(1, 0, n - 1, 0, K % n - 1);
printf("%lld\n", res.a[1][0]);
return 0;
}
/*
0 213845
4
5 4 123 4
1
8 6
0
*/
说实话思路清晰了之后其实也挺好写的。
这篇博客详细解析了CF575A Fibonacci问题,探讨了如何将该问题视为动态规划的入门示例。文章指出,如果s不变,可以直接通过矩阵乘法求解。当存在单点修改时,可以维护前缀积和后缀积。进一步推广,转变为区间查询和单点修改问题,由于矩阵逆不一定存在,因此采用线段树来维护。博主分享了线段树的具体实现细节,并指出在明确思路后,实现并不复杂。

被折叠的 条评论
为什么被折叠?



