codeforces 446C DZY Loves Fibonacci Numbers(数学 or 数论+线段树)

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 mqueries, each query has one of the two types:

  1. 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.
  2. 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.

题目大意:维护一个序列,每次给一段序列加上一个斐波那契数列,或者询问一段序列的和。

思路1:两个斐波那契的定理,用数学归纳法很容易证明:

①定义F[1] = a, F[2] = b, F[n] = F[n - 1] + F[n - 2](n≥3)。有F[n] = b * fib[n - 1] + a * fib[n - 2](n≥3),其中fib[i]为斐波那契数列的第 i 项。

②定义F[1] = a, F[2] = b, F[n] = F[n - 1] + F[n - 2](n≥3)。有F[1] + F[2] + …… + F[n] = F[n + 2] - b。

这题还有一个事实,就是两个上述定义的数列,相加,仍然符合F[n] = F[n - 1] + F[n - 2]的递推公式。

利用这两个定理,用线段树维护序列,线段树的每个结点记录这一段的前两项是什么,预处理好斐波那契数列,便能O(1)地计算出每一个结点中间的数是多少、每一个结点的和。

代码(1513MS):

 1 #include <iostream>
 2 #include <algorithm>
 3 #include <cstring>
 4 #include <cstdio>
 5 #include <cmath>
 6 using namespace std;
 7 typedef long long LL;
 8 #define ll (x << 1)
 9 #define rr ((x << 1) | 1)
10 #define mid ((l + r) >> 1)
11
12 const int MOD = 1e9 + 9;
13
14 const int MAXN = 300010;
15 const int MAXT = MAXN << 2;
16
17 int f1[MAXT], f2[MAXT], sum[MAXT];
18 int a[MAXN], fib[MAXN];
19 int n, m;
20
21 void init() {
22     fib[1] = fib[2] = 1;
23     for(int i = 3; i <= n + 2; ++i) {
24         fib[i] = fib[i - 1] + fib[i - 2];
25         if(fib[i] >= MOD) fib[i] -= MOD;
26     }
27 }
28
29 int get_fib(int a, int b, int n) {
30     if(n == 1) return a;
31     if(n == 2) return b;
32     return (LL(b) * fib[n - 1] + LL(a) * fib[n - 2]) % MOD;
33 }
34
35 int get_sum(int a, int b, int n) {
36     return (get_fib(a, b, n + 2) - b + MOD) % MOD;
37 }
38
39 void add_fib(int x, int l, int r, int a, int b) {
40     (f1[x] += a) %= MOD;
41     (f2[x] += b) %= MOD;
42     (sum[x] += get_sum(a, b, r - l + 1)) %= MOD;
43 }
44
45 void pushdown(int x, int l, int r) {
46     add_fib(ll, l, mid, f1[x], f2[x]);
47     add_fib(rr, mid + 1, r, get_fib(f1[x], f2[x], mid + 1 - l + 1), get_fib(f1[x], f2[x], mid + 2 - l + 1));
48     f1[x] = f2[x] = 0;
49 }
50
51 void maintain(int x) {
52     sum[x] = (sum[ll] + sum[rr]) % MOD;
53 }
54
55 void build(int x, int l, int r) {
56     if(l == r) {
57         sum[x] = a[l];
58     } else {
59         build(ll, l, mid);
60         build(rr, mid + 1, r);
61         maintain(x);
62     }
63 }
64
65 void update(int x, int l, int r, int a, int b) {
66     if(a <= l && r <= b) {
67         add_fib(x, l, r, fib[l - a + 1], fib[l + 1 - a + 1]);
68     } else {
69         pushdown(x, l, r);
70         if(a <= mid) update(ll, l, mid, a, b);
71         if(mid < b) update(rr, mid + 1, r, a, b);
72         maintain(x);
73     }
74 }
75
76 int query(int x, int l, int r, int a, int b) {
77     if(a <= l && r <= b) {
78         return sum[x];
79     } else {
80         int ret = 0;
81         pushdown(x, l, r);
82         if(a <= mid) (ret += query(ll, l, mid, a, b)) %= MOD;
83         if(mid < b) (ret += query(rr, mid + 1, r, a, b)) %= MOD;
84         return ret;
85     }
86 }
87
88 int main() {
89     scanf("%d%d", &n, &m);
90     for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
91     init();
92     build(1, 1, n);
93     int op, l, r;
94     while(m--) {
95         scanf("%d%d%d", &op, &l, &r);
96         if(op == 1) update(1, 1, n, l, r);
97         if(op == 2) printf("%d\n", query(1, 1, n, l, r));
98     }
99 }

思路2:按官方题解的说法,有通项公式fib[n] = sqrt(5) / 5 * (((1 + sqrt(5)) / 2) ^ n - ((1 - sqrt(5)) / 2) ^ n)。

5是1e9+9的二次剩余,383008016^2=5(mod 1e9+9)。

利用逆元,可计算出:sqrt(5) / 5、(1 + sqrt(5)) / 2、(1 - sqrt(5)) / 2在模1e9+9意义下的值。

然后,变成用线段树维护两个等比数列。预处理出(1 + sqrt(5)) / 2和(1 - sqrt(5)) / 2的1~n的次方的值,设他们为q,还要求出1-q的逆元(用于计算等比数列的和)。

线段树每个结点记录两个等比数列的首项,跟上面的方法差不多,也是这样维护一个线段树即可。

代码(1996MS):

  1 #include <iostream>
  2 #include <algorithm>
  3 #include <cstring>
  4 #include <cstdio>
  5 #include <cmath>
  6 using namespace std;
  7 typedef long long LL;
  8 #define ll (x << 1)
  9 #define rr ((x << 1) | 1)
 10 #define mid ((l + r) >> 1)
 11
 12 const int MOD = 1e9 + 9;
 13 const int SQRT5 = 383008016;
 14
 15 const int MAXN = 300010;
 16 const int MAXT = MAXN << 2;
 17
 18 int powa[MAXN], powb[MAXN];
 19 int coe, ta, tb, invta, invtb;
 20 int fa[MAXT], fb[MAXT], sum[MAXT];
 21 int a[MAXN];
 22 int n, m;
 23
 24 int inv(int x) {
 25     if(x == 1) return 1;
 26     return (LL(MOD - MOD / x) * inv(MOD % x)) % MOD;
 27 }
 28
 29 void init() {
 30     coe = inv(SQRT5);
 31     ta = (LL(1 + SQRT5) * inv(2)) % MOD;
 32     tb = (LL(1 - SQRT5 + MOD) * inv(2)) % MOD;
 33     invta = inv(1 - ta + MOD);
 34     invtb = inv(1 - tb + MOD);
 35     //cout<<coe<<endl<<ta<<endl<<tb<<endl;
 36     powa[0] = powb[0] = 1;
 37     for(int i = 1; i <= n; ++i) {
 38         powa[i] = LL(powa[i - 1]) * ta % MOD;
 39         powb[i] = LL(powb[i - 1]) * tb % MOD;
 40     }
 41 }
 42
 43 void maintain(int x) {
 44     sum[x] = (sum[ll] + sum[rr]) % MOD;
 45 }
 46
 47 void add_fib(int x, int l, int r, int a, int b) {
 48     (fa[x] += a) %= MOD;
 49     (fb[x] += b) %= MOD;
 50     (sum[x] += LL(a) * (1 - powa[r - l + 1] + MOD) % MOD * invta % MOD) %= MOD;
 51     (sum[x] -= LL(b) * (1 - powb[r - l + 1] + MOD) % MOD * invtb % MOD) %= MOD;
 52     if(sum[x] < 0) sum[x] += MOD;
 53 }
 54
 55 void pushdown(int x, int l, int r) {
 56     add_fib(ll, l, mid, fa[x], fb[x]);
 57     add_fib(rr, mid + 1, r, LL(fa[x]) * powa[mid + 1 - l] % MOD, LL(fb[x]) * powb[mid + 1 - l] % MOD);
 58     fa[x] = fb[x] = 0;
 59 }
 60
 61 void build(int x, int l, int r) {
 62     if(l == r) {
 63         sum[x] = a[l];
 64     } else {
 65         build(ll, l, mid);
 66         build(rr, mid + 1, r);
 67         maintain(x);
 68     }
 69 }
 70
 71 void update(int x, int l, int r, int a, int b) {
 72     if(a <= l && r <= b) {
 73         add_fib(x, l, r, LL(coe) * powa[l - a + 1] % MOD, LL(coe) * powb[l - a + 1] % MOD);
 74     } else {
 75         pushdown(x, l, r);
 76         if(a <= mid) update(ll, l, mid, a, b);
 77         if(mid < b) update(rr, mid + 1, r, a, b);
 78         maintain(x);
 79     }
 80 }
 81
 82 int query(int x, int l, int r, int a, int b) {
 83     if(a <= l && r <= b) {
 84         return sum[x];
 85     } else {
 86         int ret = 0;
 87         pushdown(x, l, r);
 88         if(a <= mid) (ret += query(ll, l, mid, a, b)) %= MOD;
 89         if(mid < b) (ret += query(rr, mid + 1, r, a, b)) %= MOD;
 90         return ret;
 91     }
 92 }
 93
 94 int main() {
 95     scanf("%d%d", &n, &m);
 96     for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
 97     init();
 98     build(1, 1, n);
 99     int op, l, r;
100     while(m--) {
101         scanf("%d%d%d", &op, &l, &r);
102         if(op == 1) update(1, 1, n, l, r);
103         if(op == 2) printf("%d\n", query(1, 1, n, l, r));
104     }
105 }

codeforces 446C DZY Loves Fibonacci Numbers(数学 or 数论+线段树),布布扣,bubuko.com

时间: 2024-12-26 12:18:54

codeforces 446C DZY Loves Fibonacci Numbers(数学 or 数论+线段树)的相关文章

codeforces 446C DZY Loves Fibonacci Numbers 数论+线段树成段更新

DZY Loves Fibonacci Numbers Time Limit:4000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Status Appoint description:  System Crawler  (2014-07-14) Description In mathematical terms, the sequence Fn of Fibonacci numbers is defi

codeforces 446C DZY Loves Fibonacci Numbers 线段树

假如F[1] = a, F[2] = B, F[n] = F[n - 1] + F[n - 2]. 写成矩阵表示形式可以很快发现F[n] = f[n - 1] * b + f[n - 2] * a. f[n] 是斐波那契数列 也就是我们如果知道一段区间的前两个数增加了多少,可以很快计算出这段区间的第k个数增加了多少 通过简单的公式叠加也能求和 F[n]  = f[n - 1] * b + f[n - 2] * a F[n - 1] = f[n - 2] * b + f[n - 3] * a ..

[CodeForces - 447E] E - DZY Loves Fibonacci Numbers

E  DZY Loves Fibonacci Numbers 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 consist

Codeforces446C DZY Loves Fibonacci Numbers(线段树 or 分块?)

第一次看到段更斐波那契数列的,整个人都不会好了.事后看了题解才明白了一些. 首先利用二次剩余的知识,以及一些数列递推式子有下面的 至于怎么解出x^2==5(mod 10^9+9),我就不知道了,但是要用的时候可以枚举一下,把这些参数求出来之后就题目就可以转化为维护等比数列. 由于前面的常数可以最后乘,所以就等于维护两个等比数列好了. 下面我们来看如何维护一个等比数列.假如我对区间[L,R]的加上1,2,4,8...2^n的话,那么我只需要加一个标记x表示这个区间被加了多少次这样的2^n. 举个例

Codeforces446C - DZY Loves Fibonacci Numbers

Portal Description 给出一个\(n(n\leq3\times10^5)\)个数的序列,进行\(m(m\leq3\times10^5)\)次操作,操作有两种: 给区间\([L,R]\)加上一个斐波那契数列,即\(\{a_L,a_{L+1},...,a_R\} \rightarrow \{a_L+F_1,a_{L+1}+F_2,...,a_R+F_{R-L+1}\}\) 询问区间\([L,R]\)的和,对\(10^9+9\)取模. 斐波那契数列:\(F_1=1,F_2=2\)且满足

Codeforces Round #FF (Div. 2) E. DZY Loves Fibonacci Numbers(斐波那契的定理+线段树)

/* 充分利用了菲波那切数列的两条定理: ①定义F[1] = a, F[2] = b, F[n] = F[n - 1] + F[n - 2](n≥3). 有F[n] = b * fib[n - 1] + a * fib[n - 2](n≥3),其中fib[i]为斐波那契数列的第 i 项. ②定义F[1] = a, F[2] = b, F[n] = F[n - 1] + F[n - 2](n≥3). 有F[1] + F[2] + -- + F[n] = F[n + 2] - b 这题还有一个事实,

CF446C DZY Loves Fibonacci Numbers 线段树 + 数学

code: #include <bits/stdc++.h> #define N 400004 #define LL long long #define lson now<<1 #define rson now<<1|1 #define setIO(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) using namespace

【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 -

题解 CF446C 【DZY Loves Fibonacci Numbers】

# 题解 CF446C这是一道数据结构题. 我们先翻译下题目: 给你一个n,给你一个长度为n的序列,给你一个m,给你m此操作,包括区间修改和查询,修改为在一个区间内每个数加上他所对应的斐波那契数,查询为查询区间和. 一看到区间修改和区间查询,我们就可以知道这是一道线段树的题目(不要问我怎么知道的,~~我也想知道~~) 进入正题: 我们一定要找到一种方法,维护区间和. 我们可以先把斐波那契数列的每一项拆开,找规律,我们会发现: $$f_1=f_1,f_2=f_2$$$$f_3=f_1+f_2,f_