CF575A Fibonotci 题解

这篇博客详细解析了CF575A Fibonacci问题,探讨了如何将该问题视为动态规划的入门示例。文章指出,如果s不变,可以直接通过矩阵乘法求解。当存在单点修改时,可以维护前缀积和后缀积。进一步推广,转变为区间查询和单点修改问题,由于矩阵逆不一定存在,因此采用线段树来维护。博主分享了线段树的具体实现细节,并指出在明确思路后,实现并不复杂。

CF575A Fibonotci

CF575A Fibonotci

这个题目兴许可以当做动态 D p \tt Dp Dp 入门?

考虑如果直接给定了 s s s 没有修改的话,我们直接对于这些 s s s 矩阵乘起来直接做即可。

考虑题目一个比较简单的形式,就是如果每一段只有一个位置被修改,那么我们直接维护前缀积,后缀积即可。

那么我们推广一下这个本质上就是区间查询单点修改的题目,因为矩阵的逆矩阵不一定存在,我们只能使用线段树进行维护。

我们对于每一段进行单独考虑,修改里面的所有值,之后再改回来即可。

我也不知道为什么我会在这题上耗一个上午。

具体实现方面我们考虑线段树区间变成 0 ∼ n − 1 0 \sim n - 1 0n1 对于位置 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

*/

说实话思路清晰了之后其实也挺好写的。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值