jzoj4724 斐波那契

2022-07-24 10:03:13 字數 4040 閱讀 6558

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經過長時間的學習,感覺身體被掏空,他希望你能幫他解決這個問題。

第一行兩個整數n和m,表示原始數列的長度,和總的訓練次數。

第二行n個整數a1,a2,…,an(1<=ai<=10^9) ,表示czgj給djl的原始數列。

接下來m行,每一行給出三個整數,表示問題描述中的兩種訓練的一種。保證1<=l<=r<=n 。

對於每一種形如「2 l r」的訓練,輸出一行乙個整數值。

4 41 2 3 4

1 1 4

2 1 4

1 2 4

2 1 3

1712

樣例解釋

經過第一次操作,數列變為a=[2,3,5,7] ;

第二次詢問,sum=2+3+5+7=17 ;

經過第三次操作,數列變為a=[2,4,6,9] ;

第四次詢問,sum=2+4+6=12 。

對於20%的資料,1≤n, m≤100;

對於40%的資料,1≤n, m≤1000;

對於100%的資料,1≤n, m≤100000。

無腦方法:直接暴力。

首先,上面的提示其實很好用。

我們看完題目第一直覺是用線段樹來直接維護,然而,直接是不對的,我們需要用到提示。

但是,如果你是一位數學硬傷的人士,可以看看這種方法:

我們考慮乙個定期重構的方法。(不是替罪羊樹)

首先,設做了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);

endelse

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大爺提出來的,好像很神奇,可以自己思考思考。

我不會

JZOJ4724 斐波那契

定義fi 表示斐波那契數列的第 i 項。給定乙個長度為 n的序列 a 和 m個操作。操作有兩種 data constraint n,m 105 引入一種演算法 定期重構 每次的修改操作我們不直接修改而是先將操作存起來,當已經儲存了si ze 一般取si ze n 個操作時,再進行修改 重構 每次查詢...

斐波那契數列 斐波那契數列python實現

斐波那契數列 fibonacci sequence 又稱 分割數列 因數學家列昂納多 斐波那契 leonardoda fibonacci 以兔子繁殖為例子而引入,故又稱為 兔子數列 指的是這樣乙個數列 1 1 2 3 5 8 13 21 34 在數學上,斐波納契數列以如下被以遞推的方法定義 f 1 ...

迴圈斐波那契數列 斐波那契數列應用

什麼是斐波那契數列 斐波那契數列指的是這樣乙個數列 1,1,2,3,5,8,13,21,34,55,89,144 這個數列從第3項開始,每一項都等於前兩項之和 台階問題 有一段樓梯有10級台階,規定每一步只能跨一級或兩級,要登上第10級台階有幾種不同的走法?這就是乙個斐波那契數列 登上第一級台階有一...