【CF446C】DZY Loves Fibonacci Numbers (线段树 + 斐波那契数列)

本文详细解析了一种利用线段树进行区间加斐波那契数列和区间求和的高效算法,通过巧妙转换斐波那契数列求和公式,实现对大规模数据的快速处理。

Description

看题戳我 给你一个序列,要求支持区间加斐波那契数列和区间求和。\(~n \leq 3 \times 10 ^ 5, ~fib_1 = fib_2 = 1~\).

Solution

​ 先来考虑一段斐波那契数列如何快速求和,根据性质有
\[ \begin {align} fib_n &= fib_{n - 1} + fib_{n - 2} \\ &= fib_ {n - 2} + fib_{n - 3} + fib_{n - 2} \\ &= fib_{n - 3} + fib_{n - 4} + fib_{n - 3} + fib_{n - 2} \\ &= \dots \\ &= fib_2 + \sum_{i = 1}^{n - 2} {fib_i} \end {align} \]
​ 可以发现这里有个\(~\sum_{i = 1} ^ {n - 2} {fib_i}\),转换一下就是\(~\sum_{i = 1} ^ {n}fib_i = fib_{n + 2} - fib_2\).而两个斐波那契数列对应项加起来之后还是一个类斐波那契数列,记为\(~S_i\),设这个类斐波那契数列的起始项\(S_1 = a, S_2 = b\),显然有\(~S_i = a \times fib_{i - 2} + b \times fib_{i - 1}\).那么对于一段类斐波那契数列的求和,我们只要记起始的两项和这段数列的长度即可。现在可以用简单的线段树区间加来维护了,\(~PushDown~\)操作有一点细节,注意要分开算区间的前两项。具体看代码。。

Code

#include<bits/stdc++.h>
#define For(i, j, k) for(int i = j; i <= k; ++i)
#define Forr(i, j, k) for(int i = j; i >= k; --i)
using namespace std;

inline int read() {
    int x = 0, p = 1; char c = getchar();
    for(; !isdigit(c); c = getchar()) if(c == '-') p = -1;
    for(; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x *= p;
}

inline void File() {
    freopen("cf446c.in", "r", stdin);
    freopen("cf446c.out", "w", stdout);
}

const int N = 3e5 + 10, mod = 1e9 + 9;
int n, m, fib[N];

inline int add(int a, int b) { return (a += b) >= mod ? a - mod : a; }

namespace SGT {
#define lc (rt << 1)
#define rc (rt << 1 | 1)
#define mid (l + r >> 1)
#define lson lc, l, mid
#define rson rc, mid + 1, r

    int tr[N << 2], t1[N << 2], t2[N << 2];
    
    inline void pushup(int rt) { tr[rt] = (tr[lc] + tr[rc]) % mod; }

    inline int S(int a, int b, int x) {
        return x == 1 ? a : (x == 2 ? b : (1ll * a * fib[x - 2] + 1ll * b * fib[x - 1]) % mod); 
    } 

    inline int sum(int a, int b, int x) {
        return x == 1 ? a : (x == 2 ? add(a, b) : (S(a, b, x + 2) - b + mod) % mod);    
    }

    inline void pushdown(int rt, int l, int r) {
        if (t1[rt]) {
            t1[lc] = add(t1[lc], t1[rt]), t2[lc] = add(t2[lc], t2[rt]);
            tr[lc] = add(tr[lc], sum(t1[rt], t2[rt], mid - l + 1));     
            int T1 = S(t1[rt], t2[rt], mid - l + 2), T2 = S(t1[rt], t2[rt], mid - l + 3);
            t1[rc] = add(t1[rc], T1), t2[rc] = add(t2[rc], T2);
            tr[rc] = add(tr[rc], sum(T1, T2, r - mid));
            t1[rt] = t2[rt] = 0;
        }
    }

    inline void build(int rt, int l, int r) {
        if (l == r) tr[rt] = read();
        else build(lson), build(rson), pushup(rt);
    }

    inline void update(int rt, int l, int r, int L, int R) {
        if (L <= l && r <= R) {
            tr[rt] = add(tr[rt], sum(fib[l - L + 1], fib[l - L + 2], r - l + 1));
            t1[rt] = add(t1[rt], fib[l - L + 1]); t2[rt] = add(t2[rt], fib[l - L + 2]); 
            return ;
        }   
        pushdown(rt, l, r);
        if (L <= mid) update(lson, L, R);
        if (R > mid) update(rson, L, R);
        pushup(rt);
    }

    inline int query(int rt, int l, int r, int L, int R) {
        if (L <= l && r <= R) return tr[rt];
        pushdown(rt, l, r); int res = 0;
        if (L <= mid) res = add(res, query(lson, L, R));
        if (R > mid) res = add(res, query(rson, L, R));
        return pushup(rt), res;
    }

#undef lc
#undef rc
#undef mid
#undef lson
#undef rson
}

int main() {
    File();
    n = read(), m = read();
    fib[1] = fib[2] = 1; 
    For(i, 3, n + 5) fib[i] = (fib[i - 1] + fib[i - 2]) % mod;

    using namespace SGT;
    build(1, 1, n);
    while (m --) {
        int opt = read(), l = read(), r = read();
        opt == 1 ? update(1, 1, n, l, r), 1 : printf("%d\n", query(1, 1, n, l, r)), 1;
    }

    return 0;
}

转载于:https://www.cnblogs.com/LSTete/p/9533089.html

# CF446C DZY Loves Fibonacci Numbers ## 题目描述 斐波那契数列 $f_n$ 由以下递推式定义: - $f_1=f_2=1$ - $f_n=f_{n-1}+f_{n-2}\;(n>2)$ DZY 很喜欢斐波那契数列,它给了你 $n$ 个整数 $a_1,a_2,\cdots,a_n$. 你需要执行 $m$ 个操作,操作分两种: - `1 l r`:对所有 $l\le i\le r$,将 $a_i$ 加上 $f_{i-l+1}$. - `2 l r`:求 $a_l\sim a_r$ 的和,对 $10^9+9$ 取模. ## 输入格式 第一行两个整数 $n,m$. 第二行 $n$ 个整数 $a_1,a_2,\cdots,a_n$. 接下来 $m$ 行,每行三个整数表示一个操作. ## 输出格式 对每个 $2$ 操作,一行一个整数表示答案. ## 输入输出样例 #1 ### 输入 #1 ``` 4 4 1 2 3 4 1 1 4 2 1 4 1 2 4 2 1 3 ``` ### 输出 #1 ``` 17 12 ``` ## 说明/提示 $1\le n,m\le 3\times 10^5$ $1\le a_i\le 10^9$ 标称: ```cpp #include<cstdio> #include<cctype> #define pprint(x) ::print(x),putchar(' ') #define fprint(x) ::print(x),putchar('\n') using namespace std; inline long long read() { long long x = 0;int f = 1; char ch = getchar(); for(;!isdigit(ch);ch = getchar()) if(ch == '-') f = -1; for(;isdigit(ch);ch = getchar()) x = x * 10 + (ch ^ 48); return x * f; } void print(long long x) { if(x < 0) x = -x,putchar('-'); if(x > 9) print(x / 10); putchar(x % 10 + '0'); } const int N = 300010,mod = 1000000009,q1 = 691504013,q2 = 308495997,Inv = 276601605; long long qpow(long long a,int b) { long long ans = 1; while(b) { if(b & 1) ans = ans * a % mod; b >>= 1,a = a * a % mod; } return ans; } long long Mod(long long a) { if(a > mod) a -= mod; else if(a < 0) a += mod; return a; } int n,m; struct Seg_Tree { long long q,inv,m[N]; struct tree { int l,r; long long sum,lz; }t[N << 2]; #define ls now << 1 #define rs now << 1 | 1 void build(int now,int l,int r) { t[now].l = l,t[now].r = r; t[now].sum = t[now].lz = 0; if(l == r) return; int mid = l + r >> 1; build(ls,l,mid),build(rs,mid + 1,r); } void init(int Q) { q = Q,inv = qpow(q - 1,mod - 2); m[1] = q; for(int i = 2;i <= n;i++) m[i] = m[i - 1] * q % mod; build(1,1,n); } void pusha(int now,long long a) { int l = t[now].l,r = t[now].r; t[now].lz = Mod(t[now].lz + a); t[now].sum = (t[now].sum + Mod(a * m[r - l + 1] % mod - a) * inv) % mod; } void pushdown(int now) { if(t[now].lz) { int l = t[now].l,r = t[now].r,mid = l + r >> 1; pusha(ls,t[now].lz); pusha(rs,t[now].lz * m[mid - l + 1] % mod); t[now].lz = 0; } } void pushup(int now) { t[now].sum = Mod(t[ls].sum + t[rs].sum); } void update(int now,int l,int r,int a) { if(t[now].l == l && t[now].r == r) { pusha(now,a); return; } pushdown(now); int mid = t[now].l + t[now].r >> 1; if(r <= mid) update(ls,l,r,a); else if(l > mid) update(rs,l,r,a); else update(ls,l,mid,a),update(rs,mid + 1,r,a * m[mid - l + 1] % mod); pushup(now); } long long query(int now,int l,int r) { if(t[now].l == l && t[now].r == r) return t[now].sum; pushdown(now); int mid = t[now].l + t[now].r >> 1; if(r <= mid) return query(ls,l,r); else if(l > mid) return query(rs,l,r); else return Mod(query(ls,l,mid) + query(rs,mid + 1,r)); pushup(now); } #undef ls #undef rs }; Seg_Tree t1,t2; long long sum[N]; int main() { n = read(),m = read(); for(int i = 1;i <= n;i++) sum[i] = Mod(sum[i - 1] + read()); t1.init(q1),t2.init(q2); while(m--) { int op = read(),l = read(),r = read(); if(op == 1) t1.update(1,l,r,q1),t2.update(1,l,r,q2); else { long long a = t1.query(1,l,r); long long b = t2.query(1,l,r); a = Mod(a - b) * Inv % mod; fprint(Mod(Mod(a - sum[l - 1]) + sum[r]) % mod); } } return 0; } ``` 下面代码为什么WA了 ```cpp #include <bits/stdc++.h> using namespace std; const int N = 3e5 + 50; const int mod = 1e9 + 9; const int inv2 = (mod + 1) / 2; const int qr5 = 383008016; const int q1 = 1ll * (1 + qr5) * inv2 % mod; const int q2 = 1ll * (1 - qr5 + mod) * inv2 % mod; int n, m, a[N], s[N]; int qpow(int a, int k) { int res = 1; while (k) { if (k & 1) res = 1ll * res * a % mod; a = 1ll * a * a % mod; k >>= 1; } return res; } struct SegmentTree { int pw[N], inv; int sum[N << 2], tag[N << 2]; #define ls(cur) cur << 1 #define rs(cur) cur << 1 | 1 explicit SegmentTree(int q) { pw[0] = 1; for (int i = 1; i < N; i++) pw[i] = 1ll * pw[i - 1] * q % mod; inv = qpow(q - 1, mod - 2); } void pushup(int cur) { sum[cur] = (sum[ls(cur)] + sum[rs(cur)]) % mod; } void update(int cur, int l, int r, int a) { sum[cur] = (sum[cur] + 1ll * (pw[r - l + 1] - 1) * inv % mod * a) % mod; tag[cur] = (tag[cur] + a) % mod; } void pushdown(int cur, int l, int r) { if (tag[cur]) { int mid = l + r >> 1; update(ls(cur), l, mid, tag[cur]); update(rs(cur), mid + 1, r, 1ll * tag[cur] * pw[mid - l + 1] % mod); tag[cur] = 0; } } void add(int cur, int l, int r, int L, int R, int a) { if (L == l && r == R) { update(cur, l, r, a); return ; } pushdown(cur, l, r); int mid = l + r >> 1; // if (L <= mid) // add(ls(cur), l, mid, L, mid, a); // if (mid + 1 <= R) // add(rs(cur), mid + 1, r, mid + 1, R, 1ll * a * pw[max(0, mid - L + 1)] % mod); if (R <= mid) add(ls(cur), l, mid, L, R, a); else if (L > mid) add(rs(cur), mid + 1, r, L, R, a); else { add(ls(cur), l, mid, L, mid, a); add(rs(cur), mid + 1, r, mid + 1, R, 1ll * a * pw[mid - L + 1] % mod); } pushup(cur); } int query(int cur, int l, int r, int L, int R) { if (L <= l && r <= R) return sum[cur]; pushdown(cur, l, r); int mid = l + r >> 1; int res = 0; if (L <= mid) res += query(ls(cur), l, mid, L, R); if (mid + 1 <= R) res += query(rs(cur), mid + 1, r, L, R); return res % mod; } } t1(q1), t2(q2); int main() { ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); cin >> n >> m; for (int i = 1; i <= n; i++) { cin >> a[i]; s[i] = (s[i - 1] + a[i]) % mod; } int inv = qpow(qr5, mod - 2); for (int opt, l, r; m--; ) { cin >> opt >> l >> r; if (opt == 1) { t1.add(1, 1, n, l, r, q1); t2.add(1, 1, n, l, r, q2); } else { int ans = t1.query(1, 1, n, l, r) - t2.query(1, 1, n, l, r) + mod; cout << (1ll * ans * inv + s[r] - s[l - 1] + mod) % mod << '\n'; } } return 0; } ```
最新发布
12-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值