生活随笔
收集整理的這篇文章主要介紹了
【9018题解】2109 卡德加的兔子
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
題目描述
卡德加有N個兔子籠,編號從1~N。剛開始每個兔子籠里有1對小兔,每個月小兔會長成大兔,之后的每個月這對大兔都會生出1對小兔。即兔子的繁殖遵循斐波那契數列的規律。
例如:第一個月1對小兔,第二個月1對大兔,第三個月1對小兔1對大兔,第四個月1對小兔2對大兔,第五個月2對小兔3對大兔……
卡德加學會了時間膜法,他可以對任意的[L,R]區間施加時間流逝K個月的膜法(如果他想要的話),使得這之中的兔子變為K個月后的景象。
卡德加有時想喂兔子,所以他需要知道任意的[L,R]區間內的兔子數量,即求出[L,R]區間內共有多少對兔子,大兔和小兔都需要算上。
這些操作都是在一個月內完成的,所以不必考慮自然時間流逝對兔子的影響。
輸入
第一行兩個正整數N,M,表示兔子籠的數目和操作的數目。
接下來M行,每行三個整數L,R,K。如果K>0,表示卡德加想對區間[L,R]施放時間流逝K個月的膜法。如果K=0,表示卡德加想要喂兔子了,輸出區間[L,R]內兔子總共有多少對。
輸出
對每個喂兔子的操作,輸出一行,一個數,表示兔子的對數。答案對10007取模。
樣例輸入
10 10
1 3 2
1 1 0
2 4 0
3 5 0
4 7 3
3 5 0
1 4 0
2 7 0
1 9 4
2 10 0
樣例輸出
2
5
4
8
9
16
121
題解
很顯然的線段樹類型題,但是操作不簡單。
考慮區間[L,R]內有A只小兔,B只大兔,那么經過K個月后,會有多少只呢?
根據斐波那契數列的遞推式,寫出矩陣,再寫出公式:
\(\begin{bmatrix}0&1\\1&1\end{bmatrix}^{K}\cdot\begin{bmatrix}A\\B\end{bmatrix}=\begin{bmatrix}A'\\B'\end{bmatrix}\)
A'和B'就是K月之后的小兔和大兔數量。
對于矩陣乘方,可以使用矩陣快速冪。之后就可以和普通的線段樹一樣操作。
需要注意的是,這題有點卡常,所以注意對矩陣乘法和其它細節上的常數優化。
優化①:對于2*2的小矩陣,不寫結構體,不用循環,直接乘,會加快速度。
優化②:這是一個很有技巧的優化,注意到矩陣\(\begin{bmatrix}0&1\\1&1\end{bmatrix}^{20016}\)在模10007意義下等于單位矩陣,即K=K%20016對結果沒有影響。根據這一點可以進行優化。這一點是如何想到的呢?首先應該想到,在模意義下,一切遞推式均會重復。然后可以打表找規律,找循環節。對于斐波那契數列,有定理宣稱,在模意義下,循環節長度不會超過模數的6倍。
優化③:繼續在②的基礎上優化,預處理出矩陣的0~20015次冪,做乘法時直接用,直接消滅了log和矩陣乘法的常數。
代碼如下:
1 #include<cstdio>
2 #include<cstring>
3 #define F(i,a,b) for(int i=a;i<=b;++i)
4 #define Mod 10007
//題目要求的模數
5 #define Mod2 20016
//循環節長度
6 #define getchar() (S==TT&&(TT=(S=BB)+fread(BB,1,1<<15,stdin),TT==S)?EOF:*S++)
7 char BB[
1<<
15],*S=BB,*TT=BB;
//快讀而已
8 inline
int I(){
//快讀
9 int x=
0;
char c=
getchar();
10 while(c<
'0'||c>
'9')c=
getchar();
11 while(c>=
'0'&&c<=
'9')x=(x<<
1)+(x<<
3)+c-
'0',c=
getchar();
12 return x;
13 }
14 int a1[
262145],a2[
262145],lazy[
262145];
15 int A00[
20016],A01[
20016],A10[
20016],A11[
20016];
//處理前20016次冪
16 int n,m,a,b,k,tmpa1,tmpa2;
17 inline
int mo(
int x){
return x<Mod2?x:x-
Mod2;}
18 void init(){
//預處理過程
19 A00[
0]=A11[
0]=
1, A01[
0]=A10[
0]=
0;
20 F(i,
1,
20015) A00[i]=A01[i-
1], A01[i]=(A00[i-
1]+A01[i-
1])%Mod, A10[i]=A11[i-
1], A11[i]=(A10[i-
1]+A11[i-
1])%
Mod;
21 }
22 void Mogic(
int&x,
int&y,
int K){
//對[x,y]施放時間流逝K月
23 tmpa1=(A00[K]*x+A01[K]*y)%Mod, tmpa2=(A10[K]*x+A11[K]*y)%
Mod;
24 x=tmpa1, y=
tmpa2;
25 }
26 inline
void push_down(
int l,
int r,
int i){
27 if(!lazy[i])
return;
28 Mogic(a1[i<<
1],a2[i<<
1],lazy[i]);
29 Mogic(a1[i<<
1|
1],a2[i<<
1|
1],lazy[i]);
30 lazy[i<<
1]=mo(lazy[i<<
1]+
lazy[i]);
31 lazy[i<<
1|
1]=mo(lazy[i<<
1|
1]+
lazy[i]);
32 lazy[i]=
0;
33 }
34 void build(
int l,
int r,
int i){ a1[i]=(r-l+
1)%Mod;
if(l<r) build(l,(l+r)>>
1,i<<
1), build(((l+r)>>
1)+
1,r,i<<
1|
1);}
35 void Xu(
int l,
int r,
int i){
//時間流逝
36 if(r<a||b<l)
return;
37 if(a<=l&&r<=b) { Mogic(a1[i],a2[i],k); lazy[i]=mo(lazy[i]+k);
return; }
38 push_down(l,r,i);
39 Xu(l,(l+r)>>
1,i<<
1);
40 Xu(((l+r)>>
1)+
1,r,i<<
1|
1);
41 a1[i]=(a1[i<<
1]+a1[i<<
1|
1])%
Mod;
42 a2[i]=(a2[i<<
1]+a2[i<<
1|
1])%
Mod;
43 }
44 int Feed(
int l,
int r,
int i){
//喂兔子
45 if(r<a||b<l)
return 0;
46 if(a<=l&&r<=b)
return (a1[i]+a2[i])%
Mod;
47 push_down(l,r,i);
48 return (Feed(l,(l+r)>>
1,i<<
1)+Feed(((l+r)>>
1)+
1,r,i<<
1|
1))%
Mod;
49 }
50 int main(){
51 init();
52 n=I(),m=
I();
53 build(
1,n,
1);
//建樹
54 F(i,
1,m){
55 a=I(),b=I(),k=
I();
56 if(k) {
if(k%=Mod2) Xu(
1,n,
1); }
//這里如果k是20016的倍數,就不用動了
57 else printf(
"%d\n",Feed(
1,n,
1));
58 }
59 return 0;
60 }
轉載于:https://www.cnblogs.com/PinkRabbit/p/7429964.html
總結
以上是生活随笔為你收集整理的【9018题解】2109 卡德加的兔子的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。