码迷,mamicode.com
首页 > 其他好文 > 详细

coderfoces446c (斐波那契数列)

时间:2015-10-14 19:27:16      阅读:239      评论:0      收藏:0      [点我收藏+]

标签:

题目描述:

  区间增值,但是每一项增加的值为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];

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

维护每个区间的ab的值,ab作为标记。

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

 标记会有那些操作呢?

 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 = 310000;
struct node
{
    int  lt, rt;
    int  addA,addB;
    LL   val;
}tree[4*maxN];
LL  a[maxN];
int n,m;
LL f[maxN];
LL pp[maxN];
LL qq[maxN];
void init()
{
    memset(f,0,sizeof(f));
    f[1]=1; f[2]=1;
    pp[1]=1; pp[2]=1;
    qq[1]=0; qq[2]=1;
    for(int i=3;i<maxN;i++)
    {
        f[i]=(f[i-1]+f[i-2])%MOD;
        pp[i]=(pp[i-1]+f[i-2])%MOD;
        qq[i]=(qq[i-1]+f[i-1])%MOD;
    }
}
//向下更新
void pushDown(int id)
{
    if (tree[id].addA != 0 || tree[id].addB!=0)
    {
        LL a,b;
        int LeftLen= tree[id << 1].rt - tree[id << 1].lt +1;
        a=tree[id].addA;  b=tree[id].addB;
        tree[id<<1].addA +=  a;
        tree[id<<1].addB +=  b;
        tree[id<<1].addA %=MOD;
        tree[id<<1].addB %=MOD;
        tree[id<<1].val  += ( ( (pp[LeftLen]* a)%MOD + (qq[LeftLen] *b)%MOD )%MOD ) ;
        tree[id<<1].val  %= MOD;

        int RightLen= tree[id << 1 |1].rt - tree[id <<1 |1].lt +1;
        a=( ( (tree[id].addA * f[LeftLen+ 1-2] )%MOD + (tree[id].addB * f[LeftLen +1 -1])%MOD ) %MOD );
        b=( ( (tree[id].addA * f[LeftLen+1-2 +1])%MOD + (tree[id].addB * f[LeftLen +1 -1 +1])%MOD )%MOD);
        //a和b分别为第k项和第k+1项
        tree[id<<1 |1].addA +=a;
        tree[id<<1 |1].addB +=b;
        tree[id <<1 |1].addA%=MOD;
        tree[id <<1 |1].addB%=MOD;
        tree[id<<1 |1].val  +=( ( (pp[RightLen] *a) %MOD + (qq[RightLen] *b)%MOD ) %MOD );
        tree[id <<1 |1].val%=MOD;
        tree[id].addA = 0;
        tree[id].addB = 0;
    }
}

//向上更新
void pushUp(int id)
{
    tree[id].val = ( (tree[id<<1].val + tree[id<<1|1].val) %MOD);
}

//建立线段树
void build(int lt, int rt, int id)
{
    tree[id].lt = lt;
    tree[id].rt = rt;
    tree[id].val =  0;//每段的初值,根据题目要求
    tree[id].addA = 0;
    tree[id].addB = 0;
    if (lt == rt)
    {
        tree[id].val = a[lt];
        return;
    }
    int mid = (lt+rt)>>1;
    build(lt, mid, id<<1);
    build(mid+1, rt, id<<1|1);
    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 +1;
        int plsb=   tree[id].lt - Left +2;
        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 + 1;
        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)>>1;
    if (lt <= mid)
        add2(lt, rt, id<<1, Left);
    if (rt > mid)
        add2(lt, rt, id<<1|1, 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)>>1;
    LL ans = 0;
    if (lt <= mid)
        ans += query(lt, rt, id<<1);
    if (rt > mid)
        ans += query(lt, rt, id<<1|1);
    ans%=MOD;
    return ans;
}
int main()
{
   //freopen("test.txt","r",stdin);
    init();
    while(~scanf("%d%d",&n,&m))
    {
       for(int i=1;i<=n;i++)
          scanf("%d",&a[i]);
          build(1,n,1);
       for(int i=1;i<=m;i++)
       {
           int c,l,r;
           scanf("%d%d%d",&c,&l,&r);
           if(c==1)
           {
               add2(l,r,1,l);
           }
           if(c==2)
           {
               printf("%I64d\n",query(l,r,1));
           }
       }
    }
    return 0;
}

 

coderfoces446c (斐波那契数列)

标签:

原文地址:http://www.cnblogs.com/xianbin7/p/4878220.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!