Description

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:

  1. Format of the query "1 lr". 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 lr". 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 Input

Input

4 4
1 2 3 4
1 1 4
2 1 4
1 2 4
2 1 3

Output

17
12

Sample Output

Test case #1Total explored area: 180.00

Hint

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.

这个题目是段更新的线段树,但是题目段内增加的不是固定值,是Fibonacci数列,而且每个点也不是增加的固定的某个Fibonacci值,增加的是Fi - l + 1。

目前有两种处理方式。

方式一:(等效替代)

对于Fn = Fn - 1 + Fn - 2,根据特征方程可以得到解是

Fn = (1/sqrt(5)*(((1+sqrt(5))/2)^n-((1-sqrt(5))/2)^n)

但是式子里面带有sqrt(5),double型精度丢失严重,于是这里需要一个小技巧。

对于sqrt(5),由于式子展开后不包含sqrt(5),而且sqrt(5)唯一的特征就是平方后等于5,这样就可以找一个替代的数,在模10^9+9的情况下,平方等于5就可以。这样这个数在模10^9+9的情况下,运算结果是和sqrt(5)一致的。

然后弄个程序找一下是383008016和616991993,随便一个就行。

然后就是1/sqrt(5)这个也需要在模运算下处理,这个式子的含义就是sqrt(5)的逆元,这样就弄个程序找一下逆元就可以了。

然后式子就转换成Fn = K*(X^n-Y^n)了。

就是两个等比数列了。

然后对于和s = sumFrom(1,n)(K*X^i)

可以拆分成 s = sumFrom(1,n)(K*X^i) = sumFrom(1, t)(K*X^i) + sumFrom(t+1, n)(K*X^i)

= sumFrom(1, t)(K*X^i) + X^t*sumFrom(1, n-t)(K*X^i)

这样区间段更新的时候,对每一个等比数列就满足区间的拆分了。

这样就只需要维护前面的系数k了:

刚进去时,X的系数是K,Y的系数是-K

第一次区间拆分后前一段区间是K,后一段就是X^t*K了。

这个t就是由前一段更新区间长度得到的。(这里需要注意的是,我代码里面用x和y数组已经预先处理的两个等比数列的前n项和,但是程序里在区间拆分时会出现下标访问-1的情况,这里需要特判一下。)

最后区间段更新就是求一个等比数列的前n项和,这里我预先进行了处理。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <algorithm>
#define LL long long using namespace std; //fn = (1/sqrt(5)*(((1+sqrt(5))/2)^n-((1-sqrt(5))/2)^n)
const LL MOD = 1e9+;
const LL UNIT = ;//sqrt(5)在模MOD的情况下的等价数,616991993也是
const LL X = ((+UNIT+MOD)/)%MOD;//特征根,加MOD为了化为偶数
const LL Y = (-UNIT+MOD)/;//特征根
const LL K = ;//1/sqrt(5) //线段树
//区间每点增值,求区间和
const int maxN = ;
LL x[maxN], y[maxN];
int n, m, a[maxN];
struct node
{
int lt, rt;
LL val, mul1, mul2;//mul是区间乘的值,val是区间和
}tree[*maxN]; //向下更新
void pushDown(int id)
{
LL tmp;
if (tree[id].mul1 != )
{
tree[id<<].mul1 += tree[id].mul1;
tree[id<<].mul1 %= MOD;
tree[id<<].val += x[tree[id<<].rt-tree[id<<].lt+]*tree[id].mul1%MOD;
tree[id<<].val %= MOD; tmp = x[tree[id<<].rt-tree[id<<].lt+]-x[tree[id<<].rt-tree[id<<].lt];
tmp = (tmp%MOD+MOD)%MOD;
tmp = tree[id].mul1*tmp%MOD;
tree[id<<|].mul1 += tmp;
tree[id<<|].mul1 %= MOD;
tree[id<<|].val += x[tree[id<<|].rt-tree[id<<|].lt+]*tmp%MOD;
tree[id<<|].val %= MOD;
tree[id].mul1 = ;
}
if (tree[id].mul2 != )
{
tree[id<<].mul2 += tree[id].mul2;
tree[id<<].mul2 %= MOD;
tree[id<<].val += y[tree[id<<].rt-tree[id<<].lt+]*tree[id].mul2%MOD;
tree[id<<].val %= MOD; tmp = y[tree[id<<].rt-tree[id<<].lt+]-y[tree[id<<].rt-tree[id<<].lt];
tmp = (tmp%MOD+MOD)%MOD;
tmp = tree[id].mul2*tmp%MOD;
tree[id<<|].mul2 += tmp;
tree[id<<|].mul2 %= MOD;
tree[id<<|].val += y[tree[id<<|].rt-tree[id<<|].lt+]*tmp%MOD;
tree[id<<|].val %= MOD;
tree[id].mul2 = ;
}
} //向上更新
void pushUp(int id)
{
tree[id].val = tree[id<<].val + tree[id<<|].val;
tree[id].val %= MOD;
} //建立线段树
void build(int lt, int rt, int id)
{
tree[id].lt = lt;
tree[id].rt = rt;
tree[id].val = ;//每段的初值,根据题目要求
tree[id].mul1 = tree[id].mul2 = ;
if (lt == rt)
{
tree[id].val = a[lt];
return;
}
int mid = (lt + rt) >> ;
build(lt, mid, id<<);
build(mid + , rt, id<<|);
pushUp(id);
} void add(int lt, int rt, int id, LL pls1, LL pls2)
{
pls1 = (pls1%MOD+MOD)%MOD;
pls2 = (pls2%MOD+MOD)%MOD;
if (lt <= tree[id].lt && rt >= tree[id].rt)
{
tree[id].mul1 += pls1;
tree[id].mul1 %= MOD;
tree[id].mul2 += pls2;
tree[id].mul2 %= MOD;
tree[id].val += pls1*x[tree[id].rt-tree[id].lt+]%MOD;
tree[id].val += pls2*y[tree[id].rt-tree[id].lt+]%MOD;
tree[id].val %= MOD;
return;
}
pushDown(id);
int mid = (tree[id].lt + tree[id].rt) >> ;
if (lt <= mid)
add(lt, rt, id<<, pls1, pls2);
if (rt > mid)
if (mid >= max(tree[id<<].lt,lt))
add(lt, rt, id<<|,
pls1*(x[mid-max(tree[id<<].lt,lt)+]-x[mid-max(tree[id<<].lt,lt)]),
pls2*(y[mid-max(tree[id<<].lt,lt)+]-y[mid-max(tree[id<<].lt,lt)]));
else
add(lt, rt, id<<|, pls1, pls2);
pushUp(id);
} //查询某段区间内的和
int query(int lt, int rt, int id)
{
if (lt <= tree[id].lt && rt >= tree[id].rt)
return tree[id].val;
pushDown(id);
int mid = (tree[id].lt + tree[id].rt) >> ;
int ans = ;
if (lt <= mid)
ans += query(lt, rt, id<<);
if (rt > mid)
ans += query(lt, rt, id<<|);
return ans%MOD;
} void init()
{
x[] = y[] = ;
x[] = X;
y[] = Y;
for (int i = ; i < maxN; ++i)
{
x[i] = (X*x[i-]+X)%MOD;
y[i] = (Y*y[i-]+Y)%MOD;
}
} void input()
{
for (int i = ; i <= n; ++i)
scanf("%d", &a[i]);
build(, n, );
} void work()
{
int op, from, to;
LL ans;
for (int i = ; i < m; ++i)
{
scanf("%d%d%d", &op, &from, &to);
if (op == )
add(from, to, , K, -K);
else
{
ans = query(from, to, );
printf("%I64d\n", ans);
}
}
} int main()
{
//freopen("test.in", "r", stdin);
init();
while (scanf("%d%d", &n, &m) != EOF)
{
input();
work();
}
return ;
}

方式二:(Fibonacci性质)

假设某两项a, b,那么后面的项必然是a+b, a+2b, 2a+3b......

a的系数序列为k[i] = 1, 0, 1, 1, 2.....从第0项开始,

b的系数从第一项开始。

对于f[i], f[i+1]后面某一项,f[p]

那么f[p] = k[p-i]f[i] + k[p-i+1]f[i+1]

此外,

sumFrom(lt, rt)f[p]

= sumFrom(lt, rt)(k[p-lt]f[lt]+k[p-lt+1]f[lt+1])

= f[lt]sumFrom(lr, rt)k[p-lt] + f[lt+1]sumFrom(lt, rt)k[p-lt+1]

= f[lt]s[rt-lt] + f[lt+1](s[rt-lt+1]-s[0])

此外还有一个前提是两个Fibonacci数列的和依旧是Fibonacci。

然后维护系数f[lt]和f[lt+1]就可以了。

不过依旧需要注意的是当更新区间完全在右子树时需要特殊判断u1和u2是直接传入右子树还是需要位移后传入。

代码:

 #include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <string>
#include <algorithm>
#define LL long long using namespace std; //线段树
//区间每点增值,求区间和
const LL MOD = 1e9+;
const int maxN = ;
int a[maxN], n, m;
LL k[maxN], s[maxN];
struct node
{
int lt, rt;
LL val, u1, u2;//u1,u2表示以这两个起始的Fibonacci数列
}tree[*maxN]; //向下更新
void pushDown(int id)
{
LL tmp, u1, u2;
if (tree[id].u1 != || tree[id].u2 != )
{
tree[id<<].u1 += tree[id].u1;
tree[id<<].u2 += tree[id].u2;
tree[id<<].u1 %= MOD;
tree[id<<].u2 %= MOD;
tmp = tree[id].u1*s[tree[id<<].rt-tree[id<<].lt]%MOD;
tmp += tree[id].u2*(s[tree[id<<].rt-tree[id<<].lt+]-s[])%MOD;
tmp %= MOD;
tree[id<<].val += tmp;
tree[id<<].val %= MOD; u1 = k[tree[id<<|].lt-tree[id<<].lt]*tree[id].u1%MOD;
u1 += k[tree[id<<|].lt-tree[id<<].lt+]*tree[id].u2%MOD;
u1 %= MOD;
u2 = k[tree[id<<|].lt-tree[id<<].lt+]*tree[id].u1%MOD;
u2 += k[tree[id<<|].lt-tree[id<<].lt+]*tree[id].u2%MOD;
u2 %= MOD;
tree[id<<|].u1 += u1;
tree[id<<|].u2 += u2;
tree[id<<|].u1 %= MOD;
tree[id<<|].u2 %= MOD;
tmp = u1*s[tree[id<<|].rt-tree[id<<|].lt]%MOD;
tmp += u2*(s[tree[id<<|].rt-tree[id<<|].lt+]-s[])%MOD;
tmp %= MOD;
tree[id<<|].val += tmp;
tree[id<<|].val %= MOD;
tree[id].u1 = ;
tree[id].u2 = ;
}
} //向上更新
void pushUp(int id)
{
tree[id].val = (tree[id<<].val + tree[id<<|].val)%MOD;
} //建立线段树
void build(int lt, int rt, int id)
{
tree[id].lt = lt;
tree[id].rt = rt;
tree[id].val = ;//每段的初值,根据题目要求
tree[id].u1 = tree[id].u2 = ;
if (lt == rt)
{
tree[id].val = a[lt];
return;
}
int mid = (lt + rt) >> ;
build(lt, mid, id<<);
build(mid+, rt, id<<|);
pushUp(id);
} void add(int lt, int rt, int id, int u1, int u2)
{
u1 %= MOD;
u2 %= MOD;
if (lt <= tree[id].lt && rt >= tree[id].rt)
{
LL tmp;
tree[id].u1 += u1;
tree[id].u2 += u2;
tmp = u1*s[tree[id].rt-tree[id].lt]%MOD;
tmp += u2*(s[tree[id].rt-tree[id].lt+]-s[])%MOD;
tmp %= MOD;
tree[id].val += tmp;
tree[id].val %= MOD;
return;
}
pushDown(id);
int mid = (tree[id].lt + tree[id].rt) >> ;
if (lt <= mid)
add(lt, rt, id<<, u1, u2);
if (rt > mid)
if (mid-max(lt, tree[id].lt) >= )
add(lt, rt, id<<|,
k[mid+-max(lt, tree[id].lt)]*u1%MOD+k[mid+-max(lt, tree[id].lt)]*u2%MOD,
k[mid+-max(lt, tree[id].lt)]*u1%MOD+k[mid+-max(lt, tree[id].lt)]*u2%MOD);
else
add(lt, rt, id<<|, u1, u2);
pushUp(id);
} //查询某段区间内的和
int query(int lt, int rt, int id)
{
if (lt <= tree[id].lt && rt >= tree[id].rt)
return tree[id].val;
pushDown(id);
int mid = (tree[id].lt + tree[id].rt) >> ;
int ans = ;
if (lt <= mid)
ans += query(lt, rt, id<<);
if (rt > mid)
ans += query(lt, rt, id<<|);
return ans%MOD;
} void init()
{
k[] = ;
k[] = ;
s[] = s[] = ;
for (int i = ; i < maxN; ++i)
{
k[i] = (k[i-]+k[i-])%MOD;
s[i] = (s[i-]+k[i])%MOD;
}
} void input()
{
for (int i = ; i <= n; ++i)
scanf("%d", &a[i]);
build(, n, );
} void work()
{
int op, from, to;
LL ans;
for (int i = ; i < m; ++i)
{
scanf("%d%d%d", &op, &from, &to);
if (op == )
add(from, to, , , );
else
{
ans = query(from, to, );
printf("%I64d\n", ans);
}
}
} int main()
{
//freopen("test.in", "r", stdin);
init();
while (scanf("%d%d", &n, &m) != EOF)
{
input();
work();
}
return ;
}

ACM学习历程—Codeforces 446C DZY Loves Fibonacci Numbers(线段树 && 数论)的更多相关文章

  1. Codeforces 446C DZY Loves Fibonacci Numbers [线段树,数论]

    洛谷 Codeforces 思路 这题知道结论就是水题,不知道就是神仙题-- 斐波那契数有这样一个性质:\(f_{n+m}=f_{n+1}f_m+f_{n}f_{m-1}\). 至于怎么证明嘛-- 即 ...

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

  3. codeforces 446C DZY Loves Fibonacci Numbers(数学 or 数论+线段树)(两种方法)

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

  4. Codeforces 446-C DZY Loves Fibonacci Numbers 同余 线段树 斐波那契数列

    C. DZY Loves Fibonacci Numbers time limit per test 4 seconds memory limit per test 256 megabytes inp ...

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

    DZY Loves Fibonacci Numbers Time Limit:4000MS     Memory Limit:262144KB     64bit IO Format:%I64d &a ...

  6. Codeforces 446C —— DZY Loves Fibonacci Numbers(线段树)

    题目:DZY Loves Fibonacci Numbers 题意比較简单,不解释了. 尽管官方的题解也是用线段树,但还利用了二次剩余. 可是我没有想到二次剩余,然后写了个感觉非常复杂度的线段树,还是 ...

  7. Codeforces 446C - DZY Loves Fibonacci Numbers(斐波那契数列+线段树)

    Codeforces 题目传送门 & 洛谷题目传送门 你可能会疑惑我为什么要写 *2400 的题的题解 首先一个很明显的想法是,看到斐波那契数列和 \(10^9+9\) 就想到通项公式,\(F ...

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

    第一次看到段更斐波那契数列的,整个人都不会好了.事后看了题解才明白了一些. 首先利用二次剩余的知识,以及一些数列递推式子有下面的 至于怎么解出x^2==5(mod 10^9+9),我就不知道了,但是要 ...

  9. CF446C DZY Loves Fibonacci Numbers 线段树 + 数学

    有两个性质需要知道: $1.$ 对于任意的 $f[i]=f[i-1]+f[i-2]$ 的数列,都有 $f[i]=fib[i-2]\times f[1]+fib[i-1]\times f[2]$ 其中 ...

随机推荐

  1. List集合的遍历方法

    估计你永远都不会忘记这三个方法了...... public static void main(String[] args) { //超级for循环遍历方法 List<String> lis ...

  2. dede二级导航与二级栏目 ----内容介绍二级导航

    {dede:channelartlist typeid='top'}//如果只需要拿一列,则需要使用row='1'这个属性否则会根据子频道的数目循环输出 <a href="{dede: ...

  3. 原创 | 我被面试官给虐懵了,竟然是因为我不懂Spring中的@Configuration

    GitHub 3.7k Star 的Java工程师成神之路 ,不来了解一下吗? GitHub 3.7k Star 的Java工程师成神之路 ,真的不来了解一下吗? GitHub 3.7k Star 的 ...

  4. ZooKeeper 系列(一)—— ZooKeeper核心概念详解

    一.Zookeeper简介 二.Zookeeper设计目标 三.核心概念         3.1 集群角色         3.2 会话         3.3 数据节点         3.4 节点 ...

  5. 树莓派 CPU & 主板 温度

    CPU cat /sys/class/thermal/thermal_zone0/temp | awk '{print $1/1000}' 主板 /opt/vc/bin/vcgencmd measur ...

  6. python学习(十四)面向对象

    Python中的面向对象,先写类,会生成类对象,类对象然后创建对象,对象就可以拿来用了. Python支持多重继承. class语句创建类对象,并将其赋值给变量名. class语句内的赋值语句会创建类 ...

  7. Makefile调用shell应该注意的地方

    转载:http://blog.csdn.net/ninlei0115/article/details/9732191 1.在Makefile中只能在target中调用Shell脚本,其他地方是不能输出 ...

  8. Refused to set unsafe header

    Refused to set unsafe header Refused to set unsafe header "Host"waitServerDeal @ tGet.html ...

  9. cocos2d-js添加百通广告(通过jsb反射机制)

    1.导入jar包,包括so文件 2.配置AndroidManifest.xml文件,添加: <!-- BDAPPUNIONSDK start --> <activity androi ...

  10. main方法的参数

    敲例子的时候无意中把主方法的参数给落下了,当时没有发现,保存之后就去编译,运行了,通常情况下编译没有错误那胜利就在掌握之中了,没想到这次我竟然在"不一般"的行列中,编译无误,运行出 ...