【题解】斐波拉契 luogu3938
题目
题目描述
小 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 行,每行一个正整数,依次表示你对问题的答案。
输入输出样例
输入
5
1 1
2 3
5 7
7 13
4 12
输出
1
1
2
2
4
说明

题解
分析
首先考虑70%的数据,
每天新出生的兔子数目一定是f[i],这个很容易计算得出
然后发现,这f[i]只兔子的父亲一定是1~f[i],于是模拟这个过程,做一遍LCA即可
再考虑100%的数据,
n达到int以上,无法模拟,
设,发现第s[i]+1只兔子父亲肯定是1,第s[i]+2只兔子父亲肯定是2,第
只兔子父亲一定是
于是有思路:二分s数组,使得,这时候a的父亲就是
这样的话一开始a是大于s[i]的,减过之后就小于s[i]了,至少折半,效率至多是O(logn),感觉挺快的
对b也做一遍,记录他们的“祖先历程”,然后用两个指针找一下公共祖先就可以了
实际上我是处理到的,如果小于
就直接做倍增了,这样可能快一些,不过事实证明是差不多的
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<vector>
#define ll long long
#define maxn 1000005
#define lo 21
using namespace std;
inline ll read() {
ll x=0,w=1;
char ch=getchar();
while(ch!='-'&&(ch<'-'||ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-48,ch=getchar();
return x*w;
}
int fa[lo][maxn];
ll M=1e12+50;
ll d[maxn];
ll f[maxn],sum[maxn];
vector<ll> fath[2];
int lca(int,int);
void work(ll,int);
int main() {
freopen("fibonacci.in","r",stdin);
freopen("fibonacci.out","w",stdout);
f[0]=f[1]=f[2]=1;
sum[0]=1;sum[1]=2;sum[2]=3;
fa[0][2]=fa[0][3]=1;
d[2]=d[3]=1;
register int i=3,j=0;
while(1)
{
f[i]=f[i-1]+f[i-2];
sum[i]=sum[i-1]+f[i];
if(sum[i]>M) break;
i++;
}
i=3;
while(1)
{
for(j=sum[i-1]+1; j<=sum[i]&&j<maxn; j++)
{
fa[0][j]=j-sum[i-1]; /*根据规律找父亲节点*/
d[j]=d[j-sum[i-1]]+1;
}
if(j>=maxn) break;
i++;
}
for(int k=1; k<lo; k++) for(int i=1; i<maxn; i++) fa[k][i]=fa[k-1][fa[k-1][i]];
int T=read();
for(register int i=1;i<=T;++i)
{
ll a=read(),b=read();
if(a==b)
{
printf("%lld\n",a);
continue;
}
if(a<maxn&&b<maxn) /*小于1e6的数据直接做lca*/
{
printf("%d\n",lca(a,b));
}
else
{
fath[0].clear(),fath[1].clear();
work(a,0);
work(b,1);
int i=0,j=0,flag=0;
while(i<fath[0].size()&&j<fath[1].size()) /*lca*/
{
if(fath[0][i]==fath[1][j])
{
printf("%lld\n",fath[1][j]);
flag=1;
break;
}
if(fath[0][i]>fath[1][j]) i++;
else j++;
}
if(!flag)
{
a=fath[0][fath[0].size()-1],b=fath[1][fath[1].size()-1];
printf("%d\n",lca(a,b));
}
}
}
return 0;
}
int lca(int x,int y)
{
if(d[x]<d[y]) swap(x,y);
for(register int k=d[x]-d[y],p=0; k; p++,k>>=1) if(k&1) x=fa[p][x];
if(x==y) return x;
for(register int k=lo-1; k>=0; k--) if(fa[k][x]!=fa[k][y]) x=fa[k][x],y=fa[k][y];
return fa[0][x];
}
void work(ll a,int t) /*对于大于1e6的数据找祖先*/
{
ll x=a;
fath[t].push_back(x);
while(1)
{
if(x<maxn) break;
int L=0,R=60;
while(R-L>1)
{
int mid=(L+R)/2;
if(sum[mid]<x) L=mid;
else R=mid;
}
if(sum[R]<x) x=x-sum[R];
else x=x-sum[L];
fath[t].push_back(x);
}
}
【题解】斐波拉契 luogu3938的更多相关文章
- python迭代器实现斐波拉契求值
斐波那契数列(Fibonacci sequence),又称黄金分割数列,也称为"兔子数列":F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2,n∈N*).例 ...
- 斐波拉契数列加强版——时间复杂度O(1),空间复杂度O(1)
对于斐波拉契经典问题,我们都非常熟悉,通过递推公式F(n) = F(n - ) + F(n - ),我们可以在线性时间内求出第n项F(n),现在考虑斐波拉契的加强版,我们要求的项数n的范围为int范围 ...
- 剑指offer三: 斐波拉契数列
斐波拉契数列是指这样一个数列: F(1)=1; F(2)=1; F(n)=F(n-1)+F(n); public class Solution { public int Fibonacci(int n ...
- ACM/ICPC 之 数论-斐波拉契●卢卡斯数列(HNNUOJ 11589)
看到这个标题,貌似很高大上的样子= =,其实这个也是大家熟悉的东西,先给大家科普一下斐波拉契数列. 斐波拉契数列 又称黄金分割数列,指的是这样一个数列:0.1.1.2.3.5.8.13.21.34.… ...
- 关于斐波拉契数列(Fibonacci)
斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10 ...
- 剑指offer-第二章算法之斐波拉契数列(青蛙跳台阶)
递归与循环 递归:在一个函数的内部调用这个函数. 本质:把一个问题分解为两个,或者多个小问题(多个小问题相互重叠的部分,会存在重复的计算) 优点:简洁,易于实现. 缺点:时间和空间消耗严重,如果递归调 ...
- 剑指offer-面试题9.斐波拉契数列
题目一:写一个函数,输入n,求斐波拉契数列的第n项. 斐波拉契数列的定义如下: { n=; f(n)={ n=; { f(n-)+f(n-) n>; 斐波拉契问题很明显我们会想到用递归来解决: ...
- 【斐波拉契+数论+同余】【ZOJ3707】Calculate Prime S
题目大意: S[n] 表示 集合{1,2,3,4,5.......n} 不存在连续元素的子集个数 Prime S 表示S[n]与之前的所有S[i]互质; 问 找到大于第K个PrimeS 能整除X 的第 ...
- C语言数据结构----递归的应用(斐波拉契数列、汉诺塔、strlen的递归算法)
本节主要说了递归的设计和算法实现,以及递归的基本例程斐波拉契数列.strlen的递归解法.汉诺塔和全排列递归算法. 一.递归的设计和实现 1.递归从实质上是一种数学的解决问题的思维,是一种分而治之的思 ...
随机推荐
- PHP基础-PHP中预定义的超全局数组
预定义数组: 自动全局变量---超全局数组 1. 包含了来自WEB服务器,客户端,运行环境和用户输入的数据 2. 这些数组比较特别 3. 全局范围内自动生效,都可以直接使用这些数组 4. 用户不能自定 ...
- shell 脚本中常用的内置变量
在 Bash 解释器中,内置了许多变量,这些变量的功能是解释器自带的,我们在编写shell脚本时如果能灵活的使用它们,对脚本的编写效率以及差错大有帮助, 下面一一介绍这些变量 $FUNCNAME.$L ...
- IO异步,读写压缩文件,监控文件系统
这节结尾IO,讲一下异步操作文件,读写压缩文件,监控文件系统这三个知识点. 异步操作文件: 说到异步,必然要了解的是async和await这两个关键字(异步详情点击基于任务的异步编程(Task ...
- @JsonFormat 格式化时间 时出现时间不准确问题
今天突然报个问题,简单来说说就是数据库某一字段的记录时间为 14点,然而展示到前台却是 6点 我腚眼一看,postman测试的数据也是6点 然而idea查出来的并不是6点 再仔细一瞅idea实体类的时 ...
- 运行程序显示丢失“MSVCR100D.dll”
前言 写了一个Dll注入工具,结果发现程序在其他机器上会出现丢失"MSVCR100D.dll".这个dll是vs2010自带的动态链接库,如果在没安装vs2010运行库的电脑中使用 ...
- MySQL慢日志全解析
前言: 慢日志在日常数据库运维中经常会用到,我们可以通过查看慢日志来获得效率较差的 SQL ,然后可以进行 SQL 优化.本篇文章我们一起来学习下慢日志相关知识. 1.慢日志简介 慢日志全称为慢查询日 ...
- apache common pool2原理与实战
完整源码,请帮我点个star哦! 原文地址为https://www.cnblogs.com/haixiang/p/14783955.html,转载请注明出处! 简介 对象池顾名思义就是存放对象的池,与 ...
- Jenkins 基础篇 - 插件安装
这一小节主要介绍 Jenkins 插件的安装,登录到 Jenkins 后,依次进入到[系统管理]→ [插件管理]→ [可选插件],在这里可以看到所有的 Jenkins 插件,如下图: 我们在最开始安装 ...
- Canal和Otter讨论二(原理与实践)
上次留下的问题 问题一: 跨公网部署Otter 参考架构图 解析 a. 数据涉及网络传输,S/E/T/L几个阶段会分散在2个或者更多Node节点上,多个Node之间通过zookeeper进行协同工 ...
- 马哥Linux SysAdmin学习笔记(一)
Linux入门 Linux系统管理: 磁盘管理,文件系统管理 RAID基础原理,LVM2 网络管理:TCP/IP协议,Linux网络属性配置 程序包管理:rpm,yum 进程管理:htop,glanc ...