[洛谷P3938]:斐波那契(fibonacci)(数学)
题目传送门
题目描述
小$C$养了一些很可爱的兔子。
有一天,小$C$突然发现兔子们都是严格按照伟大的数学家斐波那契提出的模型来进行繁衍:一对兔子从出生后第二个月起,每个月刚开始的时候都会产下一对小兔子。我们假定,在整个过程中兔子不会出现任何意外。
小$C$把兔子按出生顺序,把兔子们从$1$开始标号,并且小$C$的兔子都是$1$号兔子和$1$号兔子的后代。如果某两对兔子是同时出生的,那么小$C$会将父母标号更小的一对优先标号。
如果我们把这种关系用图画下来,前六个月大概就是这样的:

其中,一个箭头$A\rightarrow B$表示$A$是$B$的祖先,相同的颜色表示同一个月出生的兔子。
为了更细致地了解兔子们是如何繁衍的,小$C$找来了一些兔子,并且向你提出了$m$个问题:她想知道关于每两对兔子$a_i$和$b_i$,他们的最近公共祖先是谁。你能帮帮小$C$吗?
一对兔子的祖先是这对兔子以及他们父母(如果有的话)的祖先,而最近公共祖先是指两对兔子所共有的祖先中,离他们的距离之和最近的一对兔子。比如,$5$和$7$的最近公共祖先是$2$,$1$和$2$的最近公共祖先是$1$,$6$和$6$的最近公共祖先是$6$。
输入格式
输入第一行,包含一个正整数$m$。
输入接下来$m$行,每行包含$2$个正整数,表示$a_i$和$b_i$。
输出格式
输入一共$m$行,每行一个正整数,依次表示你对问题的答案。
样例
样例输入1:
5
1 1
2 3
5 7
7 13
4 12
样例输出1:
1
1
2
2
4
样例输入2:
10
13 5
13 6
13 7
13 8
13 9
13 10
13 11
13 12
13 13
13 14
样例输出2:
5
1
2
1
1
2
1
1
13
1
数据范围与提示
子任务会给出部分测试数据的特点。如果你在解决题目中遇到了困难,可以尝试只解决一部分测试数据。
每个测试点的数据规模及特点如下表:
测试点 $m$ $a_i,b_i$ 特殊性质$1$ 特殊性质$2$
$1$ $\leqslant 11$ $\leqslant 15$ $\surd$ $\surd$
$2$ $\leqslant 12$ $\leqslant 15$ $\times$ $\times$
$3$ $\leqslant 13$ $\leqslant 1000$ $\surd$ $\times$
$4$ $\leqslant 14$ $\leqslant 1000$ $\times$ $\times$
$5$ $\leqslant 1005$ $\leqslant{10}^6$ $\surd$ $\times$
$6$ $\leqslant 1006$ $\leqslant{10}^6$ $\times$ $\surd$
$7$ $\leqslant 1007$ $\leqslant{10}^6$ $\times$ $\times$
$8$ $\leqslant 300008$ $\leqslant{10}^{12}$ $\surd$ $\times$
$9$ $\leqslant 300009$ $\leqslant{10}^{12}$ $\times$ $\surd$
$10$ $\leqslant 300010$ $\leqslant{10}^{12}$ $\times$ $\times$
特殊性质$1$:保证$a_i,b_i$均为某一个月出生的兔子中标号最大的一对兔子。例如,对于前六个月,标号最大的兔子分别是$1,2,3,5,8,13$。
特殊性质$2$:保证$|a_i,b_i|\leqslant 1$。
题解
考场上$20$分钟切掉这道题,我感觉还是蛮爽的。
$20\%$算法:
直接手动打表,注意$14$是$1$的儿子,而$15$则是$2$的儿子,挺费眼睛的,而且一开始我打了一个表还没打对……
时间复杂度:$\Theta(m)$。
期望得分:$20$分。
$40\%$算法:
直接暴力建树,每次询问都暴力把这棵树重新建一遍来找父亲。
我觉得写这种方法的就是$\times \times \times$,不用管他……
时间复杂度:$\Theta(m\times {|a|}^2\times \log|a|)$($|a|$表示$a_i,b_i$的值域)。
期望得分:$40$分。
$60\%$算法:
考虑$QJ$两个特殊情况:
特殊情况$1$:
某一个月出生的标号最大的兔子就是斐波那契数列中的一项,画一张图,如下:

我们便会惊喜的发现如果这两只兔子同是斐波那契数列的奇数项或偶数项,那么它们的最近公共祖先就是他们当中较小的那个;否则,它们的最近公共祖先为$1$。
特殊情况$2$:
如果两只兔子的标号的绝对值等于$1$,则他们的最近公共祖先一定为$1$。
$70\%$算法:
可能细心的你会发现,一个节点一定小于自己儿子的一半,所以我们可以暴力网上找,最多有$\log i$层。
时间复杂度:$\Theta(n+m\log n)$。
期望得分:$70$分。
$100\%$算法:
在考场上,我惊喜的发现了一个规律,一个节点的减去比它小的那个斐波那契数就是他的父亲节点,所以我们可以每次让两个点中更小的那个暴力往上抬,直到相同为止即可。
时间复杂度:$\Theta(60\times m)$。
期望得分:$100$分。
代码时刻
$20\%$算法:
#include<bits/stdc++.h>
using namespace std;
int Map[16][16]={
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
0,1,2,1,1,2,1,2,1,1,2,1,1,2,1,2,
0,1,1,3,1,1,1,1,3,1,1,3,1,1,1,1,
0,1,1,1,4,1,1,1,1,1,1,1,4,1,1,1,
0,1,2,1,1,5,1,2,1,1,2,1,1,5,1,2,
0,1,1,1,1,1,6,1,1,1,1,1,1,1,1,1,
0,1,2,1,1,2,1,7,1,1,2,1,1,2,1,2,
0,1,1,3,1,1,1,1,8,1,1,3,1,1,1,1,
0,1,1,1,1,1,1,1,1,9,1,1,1,1,1,1,
0,1,2,1,1,2,1,2,1,1,10,1,1,2,1,2,
0,1,1,3,1,1,1,1,3,1,1,11,1,1,1,1,
0,1,1,1,4,1,1,1,1,1,1,1,12,1,1,1,
0,1,2,1,1,5,1,2,1,1,2,1,1,13,1,2,
0,1,1,1,1,1,1,1,1,1,1,1,1,1,14,1,
0,1,2,1,1,2,1,2,1,1,2,1,1,2,1,15};
int main()
{
int m;
scanf("%d",&m);
while(m--)
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",Map[a][b]);
}
return 0;
}
$60\%$算法:
#include<bits/stdc++.h>
using namespace std;
int m;
long long fi[60];
int getnum(long long x)
{
int res;
for(int i=1;i<=59;i++)if(fi[i]==x){res=i;break;}
return res;
}
int main()
{
scanf("%d",&m);
fi[0]=fi[1]=1;
for(int i=2;i<=59;i++)
fi[i]=fi[i-1]+fi[i-2];
for(int i=1;i<=m;i++)
{
long long a,b;
scanf("%lld%lld",&a,&b);
if(a==b){printf("%lld\n",a);continue;}
if(a-b==1||b-a==1){puts("1");continue;}
int numa=getnum(a),numb=getnum(b);
if(numa!=-1&&numb!=-1&&(numa&1)==(numb&1))printf("%lld\n",min(a,b));
else puts("1");
}
return 0;
}
$70\%$算法:
#include<bits/stdc++.h>
using namespace std;
int m;
struct rec
{
int nxt;
int to;
}e[1000001];
int head[1000001],cnt;
int t[1000001],tot=1,tmp,depth[1000001],fa[1000001][23];
bool flag;
void add(int st,int to)
{
e[++cnt].to=to;
e[cnt].nxt=head[st];
head[st]=cnt;
}
void build()
{
t[1]=1;
for(int x=1;x<30;x++)
{
tmp=tot;
for(int i=1;i<=tot;i++)
{
t[i]++;
if(t[i]>1)add(i,++tmp);
if(tmp>=1000000)return;
}
tot=tmp;
}
}
void dfs(int x)
{
for(int i=head[x];i;i=e[i].nxt)
{
if(depth[e[i].to])continue;
depth[e[i].to]=depth[x]+1;
fa[e[i].to][0]=x;
for(int j=1;j<=21;j++)
fa[e[i].to][j]=fa[fa[e[i].to][j-1]][j-1];
dfs(e[i].to);
}
}
int LCA(int x,int y)
{
if(depth[x]>depth[y])swap(x,y);
for(int i=21;i>=0;i--)
if(depth[fa[y][i]]>=depth[x])y=fa[y][i];
if(x==y)return x;
for(int i=21;i>=0;i--)
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
return fa[x][0];
}
int main()
{
scanf("%d",&m);
build();
depth[1]=1;
dfs(1);
while(m--)
{
int a,b;
scanf("%d%d",&a,&b);
printf("%d\n",LCA(a,b));
}
return 0;
}
$100\%$算法:
#include<bits/stdc++.h>
using namespace std;
int m;
long long fi[61];
long long LCA(long long x,long long y)
{
while(1)
{
if(x==y)return x;
if(x>y){for(int i=1;i<=65;i++)if(fi[i]<x&&fi[i+1]>=x){x-=fi[i];break;}}
else for(int i=1;i<=65;i++)if(fi[i]<y&&fi[i+1]>=y){y-=fi[i];break;}
}
}
int main()
{
fi[1]=fi[2]=1;
for(int i=3;i<=60;i++)
fi[i]=fi[i-1]+fi[i-2];
scanf("%d",&m);
while(m--)
{
long long a,b;
scanf("%lld%lld",&a,&b);
printf("%lld\n",LCA(a,b));
}
return 0;
}
rp++
[洛谷P3938]:斐波那契(fibonacci)(数学)的更多相关文章
- 洛谷P3938 斐波那契
题目戳 题目描述 小 C 养了一些很可爱的兔子. 有一天,小 C 突然发现兔子们都是严格按照伟大的数学家斐波那契提出的模型来进行 繁衍:一对兔子从出生后第二个月起,每个月刚开始的时候都会产下一对小兔子 ...
- 洛谷 P1306 斐波那契公约数
洛谷 P1306 斐波那契公约数 题目描述 对于Fibonacci数列:1,1,2,3,5,8,13......大家应该很熟悉吧~~~但是现在有一个很“简单”问题:第n项和第m项的最大公约数是多少? ...
- 洛谷P1962 斐波那契数列【矩阵运算】
洛谷P1962 斐波那契数列[矩阵运算] 题目背景 大家都知道,斐波那契数列是满足如下性质的一个数列: • f(1) = 1 • f(2) = 1 • f(n) = f(n-1) + f(n-2) ( ...
- 洛谷 P1306 斐波那契公约数 解题报告
P1306 斐波那契公约数 题意:求\(Fibonacci\)数列第\(n\)项和第\(m\)项的最大公约数的最后8位. 数据范围:\(1<=n,m<=10^9\) 一些很有趣的性质 引理 ...
- 洛谷——P1306 斐波那契公约数
P1306 斐波那契公约数 题目描述 对于Fibonacci数列:1,1,2,3,5,8,13......大家应该很熟悉吧~~~但是现在有一个很“简单”问题:第n项和第m项的最大公约数是多少? 输入输 ...
- 洛谷P1962 斐波那契数列 || P1349 广义斐波那契数列[矩阵乘法]
P1962 斐波那契数列 大家都知道,斐波那契数列是满足如下性质的一个数列: • f(1) = 1 • f(2) = 1 • f(n) = f(n-1) + f(n-2) (n ≥ 2 且 n 为整数 ...
- 洛谷P1755 斐波那契的拆分
题目背景 无 题目描述 已知任意一个正整数都可以拆分为若干个斐波纳契数,现在,让你求出n的拆分方法 输入输出格式 输入格式: 一个数t,表示有t组数据 接下来t行,每行一个数n(如题) 输出格式: t ...
- 洛谷——P2626 斐波那契数列(升级版)矩阵
题目背景 大家都知道,斐波那契数列是满足如下性质的一个数列: • f(1) = 1 • f(2) = 1 • f(n) = f(n-1) + f(n-2) (n ≥ 2 且 n 为整数). 题目描述 ...
- 洛谷 P2626 斐波那契数列(升级版)
题目背景 大家都知道,斐波那契数列是满足如下性质的一个数列: • f(1) = 1 • f(2) = 1 • f(n) = f(n-1) + f(n-2) (n ≥ 2 且 n 为整数). 题目描述 ...
随机推荐
- [知乎]ARM 到底是什么
[小宅按]近期公司推出来基于ARM芯片的服务器,本文就一些基本概念,比如ARM, ARM64, ARMv8, ARM7,ARMv7, 64位等让人费解的概念进行了粗浅地分析,涉及的关键字已用粗体标出. ...
- [百家号]APT组织简介2019
5家新APT组织被披露,2019是“后起之秀”的天下? https://baijiahao.baidu.com/s?id=1621699899936470038&wfr=spider& ...
- CentOS7中MySQL跨机器数据迁移
1.概况 在CentOS7环境下,使用命令方式将MySQL数据从源端主机迁移到目标端主机上. 2.迁移全部数据库 1)源端备份: [root@hadoop102 /]# mysqldump -u ro ...
- SQL如何通过当前日期获取上周一日期【转】
--当前时间 select getdate() --当前时间周的起始日期(以周一为例) ,) --上周起始: ,,)) --上上周起始: ,,)) --上上上周起始:s elect ,,))
- nginx知识问答
1.请解释一下什么是Nginx? 答:Nginx是一个web服务器和反向代理服务器,用于HTTP.HTTPS.SMTP.POP3和IMAP协议.2.请列举Nginx的一些特性? 答:Nginx服务器的 ...
- C语言--一维数组,字符数组
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/zuoyou1314/article/details/30799519 watermark/2/tex ...
- jupyter与requests的初步使用
upyter 是一个简易的,方便的写Python代码的工具包,requests是Python里非常好用的用来发送 http 请求的包. 开始学习本教程之前,请确保你已经安装了Python,并且安装了P ...
- 虚拟机的linux(CentOS)系统安装
一.安装虚拟机:VirtualBox 1.软件下载:https://www.virtualbox.org/ 2.一路点击下一步完成安装 二.安装linux:CentOS6.8 1.官方网站:ht ...
- ioncube扩展的安装详细图文教程,适合所有新手
有些程序在php环境下运行需要安装ionCube Loader的扩展支持,这里跳过介绍这些东西直接说一下怎么让你的主机环境支持这种扩展,顺利的让你的程序运行起来!这个教程适合用云服务器或者独立服务器的 ...
- ERROR: Unable to clean up existing run directory
Project Navigator - Launching PlanAhead gives: ERROR: Unable to clean up existing run directory De ...