题目描述:

区间增值,但是每一项增加的值为Fi - l + 1,F[i]为斐波那契数列,求区间和?

考虑线段树,刚开始想用斐波那契数列的前n项和,可是推不出来,考虑到每个区间的增值序列都是一段斐波那契数列,他们的和是否有什么特性呢?

发现如果前两项为a和b的话,那么,a,b,a+b,a+2b,2a+3b,3a+5b;

a和b前的系数为斐波那契数列(后一项为前两项之和,

设F[k]表示以a,b开头的第k项的值,s[k]代表以a和b开头的前k项和

F[k]=a*f[k-2]+b*f[k-1];

F[1]=1*a+0*b;

F[2]=0*a+1*b;

F[3]=f[1]*a+f[2]*b;

F[4]=f[2]*a+f[3]*b;

F[k]=f[k-2]*a+f[k-1]*b;

pp[k]=1+0+f[1]+f[2]+f[3]+...f[k-2];

qq[k]=0+f[1]+f[2]+f[3]+...+f[k-1];

求和:

S[k]=a*pp[k]+b*qq[k];

这样只需要确定每个区间的a和b,长度可以计算出来,那么第k项可以求出来,前k项和也可以求出来;

维护每个区间的a和b的值,a和b作为标记。

写这道题是发现对标记的处理有了更深的理解:

标记会有那些操作呢?

1 位置,最下层的标记以下的节点没有被更新,最上层的标记以上全都被更新过,

也就是对于每一个标记来说,它没有更新它所在节点的子节点,更新了它所有的父节点。

2 标记在同一个区间可以累加(累加型标记)

3 标记传递时,子节点的值是由父节点的标记累计改变的,子节点的标记只是用来往下传的,所以子节点的标记值没有用。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstring>
#include <algorithm>
#define LL long long
#define MOD 1000000009
using namespace std;
//线段树
//区间每点增值,求区间和
const int maxN = ;
struct node
{
int lt, rt;
int addA,addB;
LL val;
}tree[*maxN];
LL a[maxN];
int n,m;
LL f[maxN];
LL pp[maxN];
LL qq[maxN];
void init()
{
memset(f,,sizeof(f));
f[]=; f[]=;
pp[]=; pp[]=;
qq[]=; qq[]=;
for(int i=;i<maxN;i++)
{
f[i]=(f[i-]+f[i-])%MOD;
pp[i]=(pp[i-]+f[i-])%MOD;
qq[i]=(qq[i-]+f[i-])%MOD;
}
}
//向下更新
void pushDown(int id)
{
if (tree[id].addA != || tree[id].addB!=)
{
LL a,b;
int LeftLen= tree[id << ].rt - tree[id << ].lt +;
a=tree[id].addA; b=tree[id].addB;
tree[id<<].addA += a;
tree[id<<].addB += b;
tree[id<<].addA %=MOD;
tree[id<<].addB %=MOD;
tree[id<<].val += ( ( (pp[LeftLen]* a)%MOD + (qq[LeftLen] *b)%MOD )%MOD ) ;
tree[id<<].val %= MOD; int RightLen= tree[id << |].rt - tree[id << |].lt +;
a=( ( (tree[id].addA * f[LeftLen+ -] )%MOD + (tree[id].addB * f[LeftLen + -])%MOD ) %MOD );
b=( ( (tree[id].addA * f[LeftLen+- +])%MOD + (tree[id].addB * f[LeftLen + - +])%MOD )%MOD);
//a和b分别为第k项和第k+1项
tree[id<< |].addA +=a;
tree[id<< |].addB +=b;
tree[id << |].addA%=MOD;
tree[id << |].addB%=MOD;
tree[id<< |].val +=( ( (pp[RightLen] *a) %MOD + (qq[RightLen] *b)%MOD ) %MOD );
tree[id << |].val%=MOD;
tree[id].addA = ;
tree[id].addB = ;
}
} //向上更新
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].addA = ;
tree[id].addB = ;
if (lt == rt)
{
tree[id].val = a[lt];
return;
}
int mid = (lt+rt)>>;
build(lt, mid, id<<);
build(mid+, rt, id<<|);
pushUp(id);
} //增加区间内每个点固定的值
void add2(int lt, int rt, int id, int Left)
{
if (lt <= tree[id].lt && rt >= tree[id].rt)
{
int plsa= tree[id].lt - Left +;
int plsb= tree[id].lt - Left +;
tree[id].addA += f[plsa];
tree[id].addB += f[plsb];
tree[id].addA%=MOD;
tree[id].addB%=MOD;
LL a,b;
a=f[plsa]; b=f[plsb];
int Len= tree[id].rt - tree[id].lt + ;
tree[id].val +=( (a*pp[Len])%MOD + (b*qq[Len])%MOD )%MOD ;
tree[id].val %=MOD;
return;
}
pushDown(id);
//区间更新中最重要的lazy操作,把下次可能要查询的节点的标记更新到,然后只要不影响查询就好。
int mid = (tree[id].lt+tree[id].rt)>>;
if (lt <= mid)
add2(lt, rt, id<<, Left);
if (rt > mid)
add2(lt, rt, id<<|, Left);
pushUp(id);
} //查询某段区间内的和
LL query(int lt, int rt, int id)
{
if (lt <= tree[id].lt && rt >= tree[id].rt)
return tree[id].val;
pushDown(id);
//如果不pushdown的话,它的计算方式是记下沿途的标记,到达目的节点之后计算目的节点自上而下的标记之和;
//然后再加上本节点之前由自下而上的标记递推的来的和也就是tree.val(tree.val的含义就是只执行节点id及其子孙节点中的add操作,节点id对应区间中所有数之和)
//如果每次pushdown就可以把下次可能要查询的节点的标记更新到就好(tree.val的含义就是执行所有对节点id有影响的id操作,节点id对应区间中所有数之和)
int mid = (tree[id].lt+tree[id].rt)>>;
LL ans = ;
if (lt <= mid)
ans += query(lt, rt, id<<);
if (rt > mid)
ans += query(lt, rt, id<<|);
ans%=MOD;
return ans;
}
int main()
{
//freopen("test.txt","r",stdin);
init();
while(~scanf("%d%d",&n,&m))
{
for(int i=;i<=n;i++)
scanf("%d",&a[i]);
build(,n,);
for(int i=;i<=m;i++)
{
int c,l,r;
scanf("%d%d%d",&c,&l,&r);
if(c==)
{
add2(l,r,,l);
}
if(c==)
{
printf("%I64d\n",query(l,r,));
}
}
}
return ;
}

coderfoces446c (斐波那契数列)的更多相关文章

  1. C#求斐波那契数列第30项的值(递归和非递归)

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  2. 斐波拉契数列加强版——时间复杂度O(1),空间复杂度O(1)

    对于斐波拉契经典问题,我们都非常熟悉,通过递推公式F(n) = F(n - ) + F(n - ),我们可以在线性时间内求出第n项F(n),现在考虑斐波拉契的加强版,我们要求的项数n的范围为int范围 ...

  3. js中的斐波那契数列法

    //斐波那契数列:1,2,3,5,8,13…… //从第3个起的第n个等于前两个之和 //解法1: var n1 = 1,n2 = 2; for(var i=3;i<101;i++){ var ...

  4. 剑指Offer面试题:8.斐波那契数列

    一.题目:斐波那契数列 题目:写一个函数,输入n,求斐波那契(Fibonacci)数列的第n项.斐波那契数列的定义如下: 二.效率很低的解法 很多C/C++/C#/Java语言教科书在讲述递归函数的时 ...

  5. 算法: 斐波那契数列C/C++实现

    斐波那契数列: 1,1,2,3,5,8,13,21,34,....     //求斐波那契数列第n项的值 //1,1,2,3,5,8,13,21,34... //1.递归: //缺点:当n过大时,递归 ...

  6. 洛谷P1962 斐波那契数列 || P1349 广义斐波那契数列[矩阵乘法]

    P1962 斐波那契数列 大家都知道,斐波那契数列是满足如下性质的一个数列: • f(1) = 1 • f(2) = 1 • f(n) = f(n-1) + f(n-2) (n ≥ 2 且 n 为整数 ...

  7. Python递归及斐波那契数列

    递归函数 在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数.举个例子,我们来计算阶乘 n! = 1 * 2 * 3 * ... * n,用函数 fact(n)表示,可 ...

  8. 简单Java算法程序实现!斐波那契数列函数~

    java编程基础--斐波那契数列 问题描述:一只青蛙一次可以跳上1级台阶,也可以跳上2级.求该青蛙跳上一个n级的台阶总共有多少种跳法. 思路:可能出现的情况:(1) n=1 ,一种方法 ;(2)n=2 ...

  9. js 斐波那契数列(兔子问题)

    对于JS初学者来说,斐波那契数列一直是个头疼的问题,总是理不清思路. 希望看完这篇文章之后会对你有帮助. 什么是斐波那契数列 : 答: 斐波那契数列,又称黄金分割数列.因数学家列昂纳多·斐波那契(Le ...

随机推荐

  1. F题

    Problem F Codeforces 16E 这道题是一道数位Dp将鱼的死活列为0两种状态然后找DP关系 •题意:有n(n<=18)条鱼,接下来的n-1天,每天会有一对鱼(a,b)相遇,每天 ...

  2. 内存管理——(exceptional C++ 条款9,条款10)

    C++的各个内存区域: (1)常量数据(const data)区 常量数据区存储的是字符串等在编译期间就能确定的值,在整个程序的生命周期内,这里的数据都是可用.区域内所有的数据都是 只读的. (2)栈 ...

  3. msp430入门编程43

    msp430中C语言的人机交互--菜单公共函数

  4. poj2117求割点后最多的块。

    tarjan算法,枚举割点(注意此题无向图可能不连通),每个割点分割后最大块数+连通分量-1即可.开始老是TLE,后来比较了他人代码,只在vector<vector<int.>.&g ...

  5. CodeForces 596B Wilbur and Array

    简单题,一个一个操作,最后就是答案. #include<cstdio> #include<cstring> #include<cmath> #include< ...

  6. Space Ant--poj1696(极角排序)

    http://poj.org/problem?id=1696 极角排序是就是字面上的意思   按照极角排序 题目大意:平面上有n个点然后有一只蚂蚁他只能沿着点向左走  求最多能做多少点 分析:  其实 ...

  7. C/C++ (一)

    c语言中的逻辑运算符都是短路运算,一旦能够确定整个表达式的值就不再计算,配合c的定义的灵活性,可以写出很多漂亮的程序. 例如 如果要在一个长为n的数列s中找到第k个没被标记过的数 for(i=1,j= ...

  8. HDU 3001【状态压缩DP】

    题意: 给n个点m条无向边. 要求每个点最多走两次,要访问所有的点给出要求路线中边的权值总和最小. 思路: 三进制状态压缩DP,0代表走了0次,1,2类推. 第一次弄三进制状态压缩DP,感觉重点是对数 ...

  9. POJ 2502 【思维是朴素的最短路 卡输入和建图】

    题意: 给出两个坐标,分别是小明家和小明学校的坐标. 给出多条地铁线,给出每站的坐标,已知地铁是双向的,每条线以-1 -1结尾. 给出地铁速度,步行速度. 地铁线可看成是顺次连接的线段. 求小明从家到 ...

  10. composer-安装插件包

    上一步完成后,选定国内镜像地址,以为下载插件包做准备 https://pkg.phpcomposer.com/ 安装完componser后使用下面这条命令即可(设置国内镜像地址): composer ...