[luogu]P3938 斐波那契[數學]
[luogu]P3938
斐波那契
題目描述
小 C 養了一些很可愛的兔子。 有一天,小 C 突然發現兔子們都是嚴格按照偉大的數學家斐波那契提出的模型來進行 繁衍:一對兔子從出生後第二個月起,每個月剛開始的時候都會產下一對小兔子。我們假定, 在整個過程中兔子不會出現任何意外。
小 C 把兔子按出生順序,把兔子們從 1 開始標號,並且小 C 的兔子都是 1 號兔子和 1 號兔子的後代。如果某兩對兔子是同時出生的,那麽小 C 會將父母標號更小的一對優先標 號。
如果我們把這種關系用圖畫下來,前六個月大概就是這樣的:
圖來自luogu:
其中,一個箭頭 A → B 表示 A 是 B 的祖先,相同的顏色表示同一個月出生的兔子。
為了更細致地了解兔子們是如何繁衍的,小 C 找來了一些兔子,並且向你提出了 m 個 問題:她想知道關於每兩對兔子 a_iai?和 b_ibi? ,他們的最近公共祖先是誰。你能幫幫小 C 嗎?
一對兔子的祖先是這對兔子以及他們父母(如果有的話)的祖先,而最近公共祖先是指 兩對兔子所共有的祖先中,離他們的距離之和最近的一對兔子。比如,5 和 7 的最近公共祖 先是 2,1 和 2 的最近公共祖先是 1,6 和 6 的最近公共祖先是 6。
輸入輸出格式
輸入格式:
從標準輸入讀入數據。 輸入第一行,包含一個正整數 m。 輸入接下來 m 行,每行包含 2 個正整數,表示 a_iai? 和 b_ibi? 。
輸出格式:
輸出到標準輸出中。 輸入一共 m 行,每行一個正整數,依次表示你對問題的答案。
輸入輸出樣例
輸入樣例1#:
5
1 1
2 3
5 7
7 13
4 12
輸出樣例1#:
1
1
2
2
4
說明
【數據範圍與約定】 子任務會給出部分測試數據的特點。如果你在解決題目中遇到了困難,可以嘗試只解 決一部分測試數據。 每個測試點的數據規模及特點如下表:
特殊性質 1:保證 ai?,bi? 均為某一個月出生的兔子中標號最大的一對兔子。例如,對 於前六個月,標號最大的兔子分別是 1, 2, 3, 5, 8, 13。
特殊性質 2:保證∣ai?−bi?∣≤1。
我一開始還以為是有什麽公式可以算,真的是神經病。
發現每一個節點減去一個最大的斐波那契數就是其父親節點,這有很多方法可以證明,比較嚴謹的是數學歸納法。
理解的話(找規律,證明只是求個安心,oi不需要證明),其實仔細想想就會發現第n個月的數量為f(n)=f(n-1)+f(n-2),每n+1月增加f(n+1)-f(n)=f(n-1)。
註意每次新增加的節點會在1那邊開始加起,為f(n-1)+1,結束應該是下一個月連在1的開始也就應該是f(n)+1,而在這一個月是增加了f(n-1),而顯然f(n-1)就是小於[f(n-1)+1,f(n)]的最大的斐波那契數。
那我們的命題就得證,雖說可能證的不嚴謹,但的確是這麽一回事。
接下來還有比較坑爹的代碼部分,我開始也發現了這個,但不曉得會有什麽用,後來才知道就是很暴力的往上爬,爬到一樣的就行了,真的是好樸素啊。
還有就是一個個爬還會TLE,要求真多,一個數不可能減去兩個一樣的斐波那契數,所以就一次減到完,能減就減。[蒟蒻打了二分查找結果TLE]
時間復雜度:O(60m)因為f60就超過10^12,真的是可惡。
代碼:
1 //2017.11.2 2 //math 3 #include<iostream> 4 #include<cstdio> 5 #include<cstring> 6 using namespace std; 7 typedef long long ll ; 8 inline ll read(); 9 namespace lys{ 10 const ll inf = 1e12 ; 11 ll f[66]; 12 int m; 13 ll a,b; 14 void swap(ll &x,ll &y){int t=x;x=y;y=t;} 15 int main(){ 16 int i,l,r,mid; 17 scanf("%d",&m); 18 f[0]=1,f[1]=1; 19 for(i=2;i<=61;i++) f[i]=f[i-1]+f[i-2]; 20 while(m--){ 21 a=read(); b=read(); 22 for(i=60;i>=1;i--){ 23 if(a==b) break ; 24 if(a>f[i]) a-=f[i]; 25 if(b>f[i]) b-=f[i]; 26 } 27 printf("%lld\n",a); 28 } 29 return 0; 30 } 31 } 32 int main(){ 33 lys::main(); 34 return 0; 35 } 36 inline ll read(){ 37 ll kk=0,ff=1; 38 char c=getchar(); 39 while(c<‘0‘||c>‘9‘){ 40 if(c==‘-‘) ff=-1; 41 c=getchar(); 42 } 43 while(c>=‘0‘&&c<=‘9‘) kk=kk*10+c-‘0‘,c=getchar(); 44 return kk*ff; 45 }
[luogu]P3938 斐波那契[數學]