题目大意
斐波那契树,根是一个白色节点,每个白色节点都有一个黑色节点儿子,而每个黑色节点则有一个白色和一个黑色节点儿子。神奇的节点对则是指白色节点对。
请问对于深度为$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; }
|