【Codeforces Round #FF (Div. 2)】E. DZY Loves Fibonacci Numbers

題目

E. DZY Loves Fibonacci Numbers

Background
In mathematical terms, the sequence Fn of Fibonacci numbers is defined by the recurrence relation

F1 = 1; F2 = 1; Fn = Fn - 1 + Fn - 2 (n > 2).
DZY loves Fibonacci numbers very much. Today DZY gives you an array consisting of n integers: a1, a2, …, an. Moreover, there are m queries, each query has one of the two types:

Format of the query “1 l r”. In reply to the query, you need to add Fi - l + 1 to each element ai, where l ≤ i ≤ r.
Format of the query “2 l r”. In reply to the query you should output the value of modulo 1000000009 (109 + 9).
Help DZY reply to all the queries.

Input
The first line of the input contains two integers n and m (1 ≤ n, m ≤ 300000). The second line contains n integers a1, a2, …, an (1 ≤ ai ≤ 109) — initial array a.

Then, m lines follow. A single line describes a single query in the format given in the statement. It is guaranteed that for each query inequality 1 ≤ l ≤ r ≤ n holds.

Output
For each query of the second type, print the value of the sum on a single line.

Sample test(s) input
4 4
1 2 3 4
1 1 4
2 1 4
1 2 4
2 1 3
output
17
12
Note
After the first query, a = [2, 3, 5, 7].
For the second query, sum = 2 + 3 + 5 + 7 = 17.
After the third query, a = [2, 4, 6, 9].
For the fourth query, sum = 2 + 4 + 6 = 12.

思路

這是一個顯然的區間修改與查詢問題,想到使用線段樹
但是每一個區間修改的值不一樣,怎麼辦呢TAT
F1=F2=1的Fibonacci數列有以下性質

  1. Fn=Fn2+Fn1
  2. F1+F2++Fn=Fn+21

而且兩個Fibonacci數列逐項相加仍然示意個Fibonacci數列
將上述的兩個性質推廣到H1=a H2=b的Fibonacci數列,得到以下性質

  1. Hn=aFn2+bFn1
  2. H1+H2++Hn=Hn+2b

這樣就解決了區間修改問題,對於每一個區間記tag就改爲維護一個長度固定的Fibonacci數列,對每一個區間記錄這個數列的前兩項,當pushdown時也可以O(1)地修改子區間的前兩項

代碼

#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define red(i, a, b) for(int i = (a); i >= (b); i--)
#define ll long long

const int N = 333333;
const ll mod = 1000000009ll;
int n, m;
struct node {
    int l, r;
    ll sum, f1, f2;
}t[N * 6];
ll a[N], F[N];

inline ll read() {
    ll x = 0;
    char c = getchar();
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)) {
        x = x * 10ll + c - '0';
        c = getchar();
    }
    return x;
}

void calc_Fibonacci(int n) {
    F[1] = 1; F[2] = 1;
    rep(i, 3, N) F[i] = (F[i - 1] + F[i - 2]) % mod;
}

ll calc(ll a, ll b, ll n) {
    if (n == 1) return a;
    else if (n == 2) return b;
    else return (a * F[n - 2] % mod + b * F[n - 1] % mod) % mod;
}

ll calc_range(ll a, ll b, ll n) {
    if (n == 1) return a;
    else if (n == 2) return (a + b) % mod;
    else return (calc(a, b, n + 2) - b + mod) % mod;
}

void build(int x, int l, int r) {
    int mid = (l + r) / 2, lc = x * 2, rc = x * 2 + 1;
    t[x].l = l; t[x].r = r;
    if (l == r) {
        t[x].sum = a[l];
        t[x].f1 = t[x].f2 = 0;
        return;
    }
    build(lc, l, mid);
    build(rc, mid + 1, r);
    t[x].sum = (t[lc].sum + t[rc].sum) % mod;
    t[x].f1 = t[x].f2 = 0;
}

void pushdown(int x) {
    int l = t[x].l, r = t[x].r;
    int mid = (l + r) / 2, lc = x * 2, rc = x * 2 + 1;
    t[lc].f1 = (t[lc].f1 + t[x].f1) % mod;
    t[lc].f2 = (t[lc].f2 + t[x].f2) % mod;
    t[lc].sum += calc_range(t[x].f1, t[x].f2, mid - l + 1);
    t[lc].sum %= mod;
    t[rc].f1 = (t[rc].f1 + calc(t[x].f1, t[x].f2, mid - l + 2)) % mod;
    t[rc].f2 = (t[rc].f2 + calc(t[x].f1, t[x].f2, mid - l + 3)) % mod;
    t[rc].sum += calc_range(t[x].f1, t[x].f2, r - l + 1) - calc_range(t[x].f1, t[x].f2, mid - l + 1);
    t[rc].sum = (t[rc].sum + mod) % mod;
    t[x].f1 = t[x].f2 = 0;
}

void update(int x, int l, int r, int ql, int qr) {
    int mid = (l + r) / 2, lc = x * 2, rc = x * 2 + 1;
    if (l >= ql && r <= qr) {
        t[x].f1 = (t[x].f1 + F[l - ql + 1]) % mod;
        t[x].f2 = (t[x].f2 + F[l - ql + 2]) % mod;
        t[x].sum += calc_range(F[l - ql + 1], F[l - ql + 2], r - l + 1);
        t[x].sum %= mod;
        return;
    }
    pushdown(x);
    if (ql <= mid) update(lc, l, mid, ql, qr);
    if (qr > mid) update(rc, mid + 1, r, ql, qr);
    t[x].sum = (t[lc].sum + t[rc].sum) % mod;
    return;
}

ll query(int x, int l, int r, int ql, int qr) {
    int mid = (l + r) / 2, lc = x * 2, rc = x * 2 + 1;
    if (l >= ql && r <= qr) return t[x].sum;
    pushdown(x);
    ll ans_left = 0, ans_right = 0;
    if (ql <= mid) ans_left = query(lc, l, mid, ql, qr);
    if (qr > mid) ans_right = query(rc, mid + 1, r, ql, qr);
    return (ans_left + ans_right) % mod;
}

int main() {
    n = read(); m = read();
    calc_Fibonacci(n);
    rep(i, 1, n) a[i] = read();
    build(1, 1, n);
    rep(i, 1, m) {
        int tag, l, r;
        tag = read();
        l = read();
        r = read();
        if (tag == 1) update(1, 1, n, l, r);
        else printf("%lld\n", query(1, 1, n, l, r));
    }
    return 0;
}

尾聲

我的運行時間成功跑到了Accepted中的倒數第三
不過代碼長度倒是排在第一面的
調試時間之長源自於把%mod寫成%n,也是醉
這次的思路和公式寫的很好看
終於知道Markdown怎麼調字體了
SR真是Hentai呢

End.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章