zoukankan      html  css  js  c++  java
  • jzoj4724. 斐波那契

    Description

    DJL为了避免成为一只咸鱼,来找czgj学习Fibonacci数列。
    通过czgj的谆谆教导,DJL明白了Fibonacci数列是这样定义的:
    F(1)=1;F(2)=1;F(n)=F(n-1)+F(n-2)(n>2)
    Czgj深谙熟能生巧的道理,于是他给了DJL一个数列,并安排了如下的训练计划:
    1、“1 L r”,表示给ai 加上F(i-L+1) ,其中L<=i<=r ;
    2、“2 L r”,表示询问a中l到r的值的和mod 1000000009 的值。
    DJL经过长时间的学习,感觉身体被掏空,他希望你能帮他解决这个问题。

    Input

    第一行两个整数n和m,表示原始数列的长度,和总的训练次数。
    第二行n个整数a1,a2,…,an(1<=ai<=10^9) ,表示czgj给DJL的原始数列。
    接下来m行,每一行给出三个整数,表示问题描述中的两种训练的一种。保证1<=L<=r<=n 。

    Output

    对于每一种形如“2 L r”的训练,输出一行一个整数值。

    Sample Input

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

    Sample Output

    17
    12
    样例解释
    经过第一次操作,数列变为a=[2,3,5,7] ;
    第二次询问,sum=2+3+5+7=17 ;
    经过第三次操作,数列变为a=[2,4,6,9] ;
    第四次询问,sum=2+4+6=12 。

    Data Constraint

    对于20%的数据,1≤n, m≤100;
    对于40%的数据,1≤n, m≤1000;
    对于100%的数据,1≤n, m≤100000。

    Hint

    这里写图片描述

    题解

    无脑方法:直接暴力。
    首先,上面的提示其实很好用。
    我们看完题目第一直觉是用线段树来直接维护,然而,直接是不对的,我们需要用到提示。
    但是,如果你是一位数学硬伤的人士,可以看看这种方法:
    我们考虑一个定期重构的方法。(不是替罪羊树)
    首先,设做了k次操作后重构。
    重构什么意思呢?也就是把a数组给更新一下。
    那不重构的话怎么弄呢?
    我们设一个d数组,表示一个记录修改操作的数组。这个数组可以利用差分的方式来维护。
    什么意思呢?对于一次插入操作l,r
    我们直接在d[l]的位置加1
    在d[r+1]的位置加上f[r-l+2](斐波那契数列)
    在d[r+2]的位置加上f[r-l+1]
    这样我们一条线扫过去,每次把d做一遍类似斐波那契的方法就可以求出若干次操作后对a的影响为什么。
    也就是d[i]:=d[i]+d[i-1]+d[i-2];
    这样的话可以在重构的时候在n的时间复杂度内更新a数组。

    那么对于一个插入操作l,r
    我们可以先在a中提取值,然后再在未重构的插入操作里面计算。
    那么我们就直接暴力枚举未重构的插入操作,然后更新答案即可。

    正确性显然,但是时间复杂度好像很玄学。
    细细推就是:O(mn/k+km)
    然后我们设k为根号n,那么就是√n的时间复杂度。

    标程:

    uses math;
    const up=1000000;
    var
            i,j,k,l,r,n,m,sx,t,now:longint;
            a,x,y,kind,sum,fei,sumf,d:array[0..up] of int64;
            mo,ans:int64;
    begin
            assign(input,'fibonacci.in');reset(input);
            assign(output,'fibonacci.out');rewrite(output);
            mo:=1000000009;
            fei[1]:=1;
            fei[2]:=1;
            sumf[1]:=1;
            sumf[2]:=2;
            for i:=3 to up do
            begin
                    fei[i]:=(fei[i-1]+fei[i-2]) mod mo;
                    sumf[i]:=(sumf[i-1]+fei[i]) mod mo;
            end;
            readln(n,m);
            for i:=1 to n do
            begin
                    read(a[i]);
                    sum[i]:=(sum[i-1]+a[i]) mod mo;
            end;
            readln;
            for i:=1 to m do
            begin
                    readln(kind[i],x[i],y[i]);
            end;
            sx:=trunc(sqrt(m));
            now:=1;
            for i:=1 to m do
            begin
                    if kind[i]=2 then
                    begin
                            ans:=(sum[y[i]]-sum[x[i]-1]+mo) mod mo;
                            for j:=now to i do
                            begin
                                    if kind[j]=1 then
                                    begin
                                            if (y[j]>=x[i]) and (x[j]<=y[i]) then
                                            begin
                                                    ans:=(ans+sumf[min(y[i],y[j])-x[j]+1]-sumf[max(x[i],x[j])-x[j]]+mo) mod mo;
                                            end;
                                    end;
                            end;
                            writeln(ans);
                    end
                    else
                    if kind[i]=1 then
                    begin
                            inc(d[x[i]]);
                            dec(d[y[i]+1],fei[y[i]-x[i]+2]);
                            dec(d[y[i]+2],fei[y[i]-x[i]+1]);
                    end;
                    if i mod sx=0 then
                    begin
                            a[1]:=a[1]+d[1];
                            for j:=2 to n do
                            begin
                                    d[j]:=(d[j]+d[j-1]+d[j-2]+mo) mod mo;
                                    a[j]:=(a[j]+d[j]+mo) mod mo;
                            end;
                            for j:=1 to n do
                            begin
                                    sum[j]:=(sum[j-1]+a[j]) mod mo;
                            end;
                            fillchar(d,sizeof(d),0);
                            now:=now+sx;
                    end;
            end;
    end.
    

    然而,√n算法没那么优秀对吧?
    那么我就来讲讲神奇的log 算法
    于是乎,我们就回到提示之中去:
    我们看到前两个,那么我们就可以发现下面的这些东东:
    这里写图片描述
    是不是很神奇。
    于是乎,我们可以发现,斐波那契的公比是相等的。
    也就是说可以下面这样:
    这里写图片描述
    上下两个东东可以相加的。
    这个时候再考虑线段树是不是比较清晰明了了?
    但,实现起来还是有两个东东卡死了——
    1、假如多个操作区间重叠在一起,如何快速计算重叠在一起的答案
    2、而且是不是每次下传就要把这几个区间的左端点一齐记录,不然时空都很玄
    然后,如果你会用第三个提示,那就用。
    可是第四个提示十分清晰明了地可以解决这两个问题:
    根据提示,我们发现,可以由最前面的两个推出后面的东东。
    那么就计录出前两项即可。
    由于是可以加起来的,所以不必担心第二个问题。

    这个时候考虑线段树就极好了。
    但是我没有码

    还有一种线段树维护矩阵的方法。
    597大爷提出来的,好像很神奇,可以自己思考思考。
    我不会

    我活在这夜里。无论周围多么黑暗,我都要努力发光!我相信着,终有一天,我会在这深邃的夜里,造就一道最美的彩虹。
  • 相关阅读:
    PowerTalk第一个版本儿控件
    PowerTalk第二个版本,支持(Msn回复信息)
    自然语言处理著作或期刊名称
    自然语言处理(NLP)网上资源整理 (转)
    TFIDF
    计算机科学及工程
    自然语言处理与计算语言学书籍汇总之一:国外书籍
    UVa 10696 f91
    缓存
    操作必须使用一个可更新的查询(转)
  • 原文地址:https://www.cnblogs.com/RainbowCrown/p/11148393.html
Copyright © 2011-2022 走看看