洛谷P3938 斐波那契
题目戳
题目描述
小 C 养了一些很可爱的兔子。 有一天,小 C 突然发现兔子们都是严格按照伟大的数学家斐波那契提出的模型来进行 繁衍:一对兔子从出生后第二个月起,每个月刚开始的时候都会产下一对小兔子。我们假定, 在整个过程中兔子不会出现任何意外。
小 C 把兔子按出生顺序,把兔子们从 1 开始标号,并且小 C 的兔子都是 1 号兔子和 1 号兔子的后代。如果某两对兔子是同时出生的,那么小 C 会将父母标号更小的一对优先标 号。
如果我们把这种关系用图画下来,前六个月大概就是这样的:
其中,一个箭头 A → B 表示 A 是 B 的祖先,相同的颜色表示同一个月出生的兔子。
为了更细致地了解兔子们是如何繁衍的,小 C 找来了一些兔子,并且向你提出了 m 个 问题:她想知道关于每两对兔子 aia_iai 和 bib_ibi ,他们的最近公共祖先是谁。你能帮帮小 C 吗?
一对兔子的祖先是这对兔子以及他们父母(如果有的话)的祖先,而最近公共祖先是指 两对兔子所共有的祖先中,离他们的距离之和最近的一对兔子。比如,5 和 7 的最近公共祖 先是 2,1 和 2 的最近公共祖先是 1,6 和 6 的最近公共祖先是 6。
输入输出格式
输入格式:
从标准输入读入数据。 输入第一行,包含一个正整数 m。 输入接下来 m 行,每行包含 2 个正整数,表示 aia_iai 和 bib_ibi 。
输出格式:
输出到标准输出中。 输入一共 m 行,每行一个正整数,依次表示你对问题的答案。
输入输出样例
说明
【数据范围与约定】 子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试只解 决一部分测试数据。 每个测试点的数据规模及特点如下表:
特殊性质 1:保证 aia_iai, bib_ibi 均为某一个月出生的兔子中标号最大的一对兔子。例如,对 于前六个月,标号最大的兔子分别是 1, 2, 3, 5, 8, 13。
特殊性质 2:保证 ∣ai−bi∣≤1|a_i-b_i|\le 1∣ai−bi∣≤1。
Solution:
题目来自于洛谷的一次月赛,考的时候在ka哥提醒下AC,值得说这题其实很容易,关键是思路。
解法:斐波拉契
思路:很多人直接诶想到了lca吧,但对这道题显然是不可以的。我们考虑列出几项斐波拉契数来查找规律:[1] [2] [3] [4 5] [6 7 8] [9 10 11 12 13]…我们观察一下上述的几项,同一个[]中的是同时出生的,我们发现第i个月出生的兔子恰巧就是它上个月之前的兔子所生,而且对于一个数x,它的直接父亲就是x-fi,所以我们可以对于每次询问(a,b),将a、b中较大的数先往前跳到它的父亲,然后比较此时a和b的大小是否相同,若想同则输出该数,若不同则重复上述步骤继续往前跳。说的好像不太清楚,但是我们自己列一列应该很容易看出。举个例子:假设我要询问8和11的最近公共祖先是谁,我们先对11往前跳,即11-8得到了3,此时比较3和8的大小,不相等,so用较大的数8继续往前跳,即8-5=3,此时3和3相等了,所以3就是11和8的最近公共祖先。
注意:题目中数据较大到了10的12次方,所以要开long long,此外必须得预处理出斐波拉契中的前60项(因为数据只有10的12次方,斐波拉契的第60项刚好超过了数据范围),然后就是读入优化(数据有300多万次询问而且数还那么大),最后在比较时最好二分查找节省时间。
代码量超少,关键是思路稍微得思考
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define il inline 5 ll m,a,b; 6 il ll gi() 7 { 8 int a=0;char x=getchar();bool f=0; 9 while((x<'0'||x>'9')&&x!='-')x=getchar(); 10 if(x=='-')x=getchar(),f=1; 11 while(x>='0'&&x<='9')a=a*10+x-48,x=getchar(); 12 return f?-a:a; 13 } 14 ll c[100]; 15 il void find(ll a,ll b) 16 { 17 if(a<b)swap(a,b); 18 if(a==b){printf("%lld\n",a);return;} 19 int w=lower_bound(c,c+62,a)-c; 20 find(b,a-c[w-1]); 21 } 22 int main() 23 { 24 m=gi(); 25 c[0]=1;c[1]=1; 26 for(int i=2;i<=61;i++)c[i]=c[i-1]+c[i-2]; //printf("%lld\n",c[i]); 27 while(m--) 28 { 29 a=gi(),b=gi(); 30 find(a,b); 31 } 32 return 0; 33 }