luoguP3938 斐波那契

传送门

题解

懒得做题,又不想颓,所以水一篇题解。

首先,要想做这道题,就要知道一个规律:父亲总是比儿子少上前一个斐波那契数。怎么来的呢?当然可以打表,这也是最简单的做法。(然鹅这个人考试的时候连表都懒得打,还爆零了)

当然,仅仅是知道结果对于有着强迫症的人来说是不能忍受的。所以最好的方法就是证一下。(然鹅这个人只是在无耻地口胡)

照着图,我们分别列出写出前六个月的月份、斐波那契数、新诞生的兔兔的编号((F_0=1)已省略):

一月 (F_1=1) 1
二月 (F_2=2) 2
三月 (F_3=3) 3
四月 (F_4=5) 4 5
五月 (F_5=8) 6 7 8
六月 (F_6=13) 9 10 11 12 13

可以看见,第(N)个月新生的兔兔的数量就是(F_{N-2}),而他们的父亲分别是从(1)号到(F_{N-2})号,那么由于中间隔了(F_{N-1})代兔兔,则第(N)代每只新生兔兔的父亲都应是其编号减去(F_{N-1}),这充分利用了每只新生兔兔的编号都是按其父亲的编号由小到大而来的。

然后,对于给出的每两只兔兔,我们按此规律令它们不断交替向上跳,即可找到答案,过程中因为斐波那契单调递增可以用二分优化。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100;
#define ll long long
char buf[1 << 20], *p1, *p2;
char getc() {
	if(p1 == p2) {
		p1 = buf;
		p2 = buf + fread(buf, 1, 1 << 20, stdin);
		if(p1 == p2) return EOF;
	}
	return *p1++;
}
inline ll read() {
	ll s = 0, w = 1; char c = getc();
	while(c < '0' || c >'9') {if(c == '-')w = -1; c = getc();}
	while(c >= '0' && c <= '9') s = s * 10 + c - '0', c = getc();
	return s * w;
}
ll fac[maxn], m, a, b;
int main() {
    m = read(); 
    fac[0] = 1; 
    fac[1] = 1;
    for(register int i = 2; i <= 60; i++)//斐波那契的前60项就已达到数据要求
    	fac[i] = fac[i - 1] + fac[i - 2];     
    while(m--) {
        a = read(), b = read();
        while(a != b) {
        	if(a < b) swap(a, b);
        	ll l = 1, r = 60, mid;
        	while(l <= r) {
        		mid = (l + r) >> 1;
        		if(fac[mid] < a) l = mid + 1;
        		else r = mid - 1;        		
        	}
        	ll z = a;
        	a = b;
        	b = z - fac[l - 1];
        }
        if(a == b)
        	printf("%lld
", a);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Zfio/p/13427939.html