zoukankan      html  css  js  c++  java
  • coderfoces446c (斐波那契数列)

    题目描述:

      区间增值,但是每一项增加的值为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
    ",query(l,r,1));
               }
           }
        }
        return 0;
    }
  • 相关阅读:
    hdu acm 2844 Coins 解题报告
    hdu 1963 Investment 解题报告
    codeforces 454B. Little Pony and Sort by Shift 解题报告
    广大暑假训练1 E题 Paid Roads(poj 3411) 解题报告
    hdu acm 2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活
    hdu acm 1114 Piggy-Bank 解题报告
    poj 2531 Network Saboteur 解题报告
    数据库范式
    ngnix 配置CI框架 与 CI的简单使用
    Vundle的安装
  • 原文地址:https://www.cnblogs.com/xianbin7/p/4878220.html
Copyright © 2011-2022 走看看