隐藏
「NOIP十连赛day8」幻魔皇 - 递推 | Bill Yang's Blog

路终会有尽头,但视野总能看到更远的地方。

0%

「NOIP十连赛day8」幻魔皇 - 递推

题目大意

斐波那契树,根是一个白色节点,每个白色节点都有一个黑色节点儿子,而每个黑色节点则有一个白色和一个黑色节点儿子。神奇的节点对则是指白色节点对。
请问对于深度为$n$的斐波那契树,其中距离为$i$的神奇节点对有多少个?拉比艾尔需要你对于$1\le i\le 2n$的所有$i$都求出答案。


题目分析

为什么叫斐波那契树呢?我们画个图看看。

有没有发现白点、黑点后期均为斐波那契数列?
这是一个非常好的性质。
设$Black[i],White[i]$分别表示第$i$层的黑/白点个数,$sumb[i],sumw[i]$分别是它们的前缀和。

考虑求出答案$Ans[i]$。
分$lca$为黑点、白点进行讨论。

当$lca$为白点时,此时没有分叉,因此必定是使用白点下方距离为$i$的白点统计答案。
故$Ans[i]+=sumw[n-i]\times White[i+1]$

当$lca$为黑点时,此时有两个儿子,考虑将左边白色儿子长度为$i$的与右边黑色儿子长度为$j$的合并起来更新答案$Ans[i+j]$。
这样的黑点$lca$有$sumb[n-\max(i,j)]$个,因此$Ans[i+j]+=sumb[n-\max(i,j)]\times White[i]\times White[j+1]$


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;
typedef long long LL;
inline const LL Get_Int() {
LL num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}
const int maxn=5005;
const LL mod=123456789;
int n;
LL Black[maxn],White[maxn],sumb[maxn],sumw[maxn],ans[maxn*2];
int main() {
n=Get_Int();
Black[2]=1;
White[1]=1,White[2]=0;
for(int i=3; i<=n; i++) {
White[i]=(White[i-1]+White[i-2])%mod;
Black[i]=(Black[i-1]+Black[i-2])%mod;
}
for(int i=1; i<=n; i++) {
sumw[i]=(sumw[i-1]+White[i])%mod;
sumb[i]=(sumb[i-1]+Black[i])%mod;
}
for(int i=1; i<n; i++)ans[i]=sumw[n-i]*White[i+1]%mod;
for(int i=1; i<n; i++)
for(int j=1; j<n; j++)
ans[i+j]=(ans[i+j]+(sumb[n-max(i,j)])*White[i]%mod*White[j+1])%mod;
for(int i=1; i<=2*n; i++)printf("%d ",ans[i]);
return 0;
}
姥爷们赏瓶冰阔落吧~