首页 > 代码库 > codeforces 446C DZY Loves Fibonacci Numbers(数学 or 数论+线段树)

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 "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 "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 }
View Code

 

思路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 }
View Code