模拟9:T1:斐波那契
Description:
题目描述:
小 C 养了一些很可爱的兔子。
有一天,小 C 突然发现兔子们都是严格按照伟大的数学家斐波那契提出的模型来进行繁衍:一对兔子从出生后第二个月起,每个月刚开始的时候都会产下一对小兔子。我们假定,在整个过程中兔子不会出现任何意外。
小 C 把兔子按出生顺序,把兔子们从 1 开始标号,并且小 C 的兔子都是 1 号兔子和 1号兔子的后代。如果某两对兔子是同时出生的,那么小 C 会将父母标号更小的一对优先标号。
如果我们把这种关系用图画下来,前六个月大概就是这样的:
其中,一个箭头 A→B 表示 A 是 B 的祖先,相同的颜色表示同一个月出生的兔子。
为了更细致地了解兔子们是如何繁衍的,小 C 找来了一些兔子,并且向你提出了 个问题:她想知道关于每两对兔子 和 ,他们的最近公共祖先是谁。你能帮帮小 C 吗? 一对兔子的祖先是这对兔子以及他们父母(如果有的话)的祖先,而最近公共祖先是指两对兔子所共有的祖先中,离他们的距离之和最近的一对兔子。比如,5 和 7 的最近公共祖先是 2,1 和 2 的最近公共祖先是 1,6 和 6 的最近公共祖先是 6。
输入格式
输入第一行,包含一个正整数。
输入接下来m行,每行包含 2 个正整数,表示 和。
输出格式
输出一共行,每行一个正整数,依次表示你对问题的答案。
样例
样例输入
5
1 1
2 3
5 7
7 13
4 12
样例输出
1
1
2
2
4
数据范围:
这个题题目暗示的太明显了,斐波那契数。
先说一下70分解法:
我们观察图中的树,可以发现,每一代的子代个数满足斐波那契数(从第二代开始)
其实可以推的:
每一代增加的兔子数是上数第二代的兔子数,因为每一只兔子一个月只生一只,且从出生第二个月开始生育,那么就有这一代新增兔子的表达式:
f[i]=f[i-1]+f[i-2]
i表示第几代子代,i从3开始(第一、二代不用公式手模也知道啊(逃));
并且,编号小的兔子的子代编号也小,那么就这一代的兔子从小往大里父亲是1、2……
然后就可以建树了。
建树后就可以直接跑LCA了。70分到手。
这是作者本人考场上的想法。
70分代码:
#include<bits/stdc++.h>
using namespace std;
namespace Decleration
{
#define ll long long
#define rr register
#define pp pair<int,int>
const int SIZEM=300014,SIZEAB=1e6+4;
int m,maxn=INT_MIN;
int a[SIZEM],b[SIZEM];
int to[SIZEAB<<1],last[SIZEAB];
int dire[SIZEAB<<1];
queue<int> q;
inline int max(rr int x,rr int y){return x>y?x:y;}
inline void add(int f,int t)
{
static int num=0;
*(to+(++num))=t;
*(dire+num)=*(last+f);
*(last+f)=num;
}
void build(int x)
{
int now=1,en;
q.push(1);
while(1)
{
int fi=1,se=1,l=1;
while(1)
{
int cnt=0;
int t=se+fi;
for(rr int i=l+1;i<=t;i++)
add(++cnt,i),add(i,cnt);
fi=se;
se=t;
l=t;
if(t>=x) return;
}
}
}
int read()
{
rr int x_read=0,y_read=1;
rr char c_read=getchar();
while(c_read<'0'||c_read>'9')
{
if(c_read=='-') y_read=getchar();
c_read=getchar();
}
while(c_read<='9'&&c_read>='0')
{
x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
};
using namespace Decleration;
namespace L_C_A
{
int fa[SIZEAB],son[SIZEAB];
int size[SIZEAB],top[SIZEAB];
int depth[SIZEAB];
void dfs1(int x)
{
size[x]=1;
for(rr int i=last[x];i;i=dire[i])
{
if(to[i]==fa[x]) continue;
fa[to[i]]=x;
depth[to[i]]=depth[x]+1;
dfs1(to[i]);
size[x]+=size[to[i]];
if(size[to[i]]>size[son[x]]) son[x]=to[i];
}
}
void dfs2(int x)
{
if(x==son[fa[x]]) top[x]=top[fa[x]];
else top[x]=x;
for(rr int i=last[x];i;i=dire[i])
{
if(to[i]==fa[x]) continue;
dfs2(to[i]);
}
}
int LCA(int x,int y)
{
while(top[x]!=top[y])
{
if(depth[top[x]]>depth[top[y]]) x=fa[top[x]];
else y=fa[top[y]];
}
if(depth[x]<depth[y]) return x;
return y;
}
};
using namespace L_C_A;
int main()
{
m=read();
for(rr int i=1;i<=m;i++)
{
a[i]=read(),b[i]=read();
maxn=max(maxn,a[i]),maxn=max(maxn,b[i]);
}
build(maxn);
dfs1(1);
dfs2(1);
for(rr int i=1;i<=m;i++)
printf("%d\n",LCA(a[i],b[i]));
}
这里我用的树链剖分求的LCA,比ST快。
这个代码仅供参考,我考场上只得了50分RE了,估计是哪里没写好,大家以此为思路参考就好了。
接下来说正解。
我们既然已经想出来那个式子了,按理说正解就顺理成章了(然而我考试没多想,那70分就走人了),我们可以发现,还是根据那个式子,每一个点减去比他小的斐波那契数中最大的那一个得到的就是他的父亲,那么我们就可以利用这个性质,一点一点往上跳父亲找LCA,100分到手。
#include<bits/stdc++.h>
using namespace std;
namespace Decleration
{
#define ll long long
#define rr register
const int SIZEM=300014;
ll m;
ll f[64];
ll read()
{
rr ll x_read=0,y_read=1;
rr char c_read=getchar();
while(c_read<'0'||c_read>'9')
{
if(c_read=='-') y_read=getchar();
c_read=getchar();
}
while(c_read<='9'&&c_read>='0')
{
x_read=(x_read<<3)+(x_read<<1)+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
int bound(rr ll x)
{
int l=1,r=60;
while(l<r)
{
rr int mid=(l+r+1)>>1;
if(f[mid]<=x) l=mid;
else r=mid-1;
}
if(f[l]==x) l--;
return l;
}
ll LCA(rr ll a,rr ll b)
{
int i1=bound(a),i2=bound(b);
while(a!=b)
{
if(i1>i2) a-=f[i1],i1=bound(a);
//i大意味着深度大,优先跳深度大的,为什么,我不解释,留给您自己思考
else b-=f[i2],i2=bound(b);
}
return a;
}
};
using namespace Decleration;
int main()
{
m=read();
f[1]=f[2]=1;
for(rr int i=3;i<=62;i++)
f[i]=f[i-1]+f[i-2];
//第60个斐波那契数就已经大于a,b的上限了,当然,也爆long long了
for(rr int i=1;i<=m;i++)
{
ll a=read(),b=read();
printf("%lld\n",LCA(a,b));
}
}
2021.6.22 现役