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級台階有幾種不同的走法?這就是乙個斐波那契數列 登上第一級台階有一...