【NOIP2017提高组模拟12.10】幻魔皇

来源:互联网 发布:linux ntp配置文件 编辑:程序博客网 时间:2024/05/21 17:32

Description

幻魔皇拉比艾尔很喜欢斐波那契树,他想找到神奇的节点对。
所谓斐波那契树,根是一个白色节点,每个白色节点都有一个黑色节点儿子,而每个黑色节点则有一个白色和一个黑色节点儿子。神奇的节点对则是指白色节点对。
请问对于深度为n的斐波那契树,其中距离为i的神奇节点对有多少个?拉比艾尔需要你对于1<=i<=2n的所有i都求出答案。

Solution

把一段路径拆成两段,x->lca,lca->y。
我们考虑lca的颜色。
当lca为白色的时候,那么只会有从下面到lca的路径,那么个数就是第i层白色节点的个数(找一下规律是斐波那契额数列)
当lca为黑色的时候,我们枚举左儿子的长度k,那么右儿子的长度就是i-k,然后求出这些白色节点的个数然后再乘以黑色节点的个数[枚举有儿子距离有i-k和k,那么把这个层数一下的节点加起来](也是斐波那契额数列,但是要求一下前缀和)。
但是会有一个问题,就是直接求的话,右儿子中有一些节点是左儿子里面的,那么就会算重。
所以要减去左儿子重复的情况,那么就设左儿子是白的,那么从右儿子的黑的开始,右儿子就算i-k-1深度的白节点数。

Code

#include<iostream>#include<stdio.h>#include<string.h>#include<math.h>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)using namespace std;const int maxn=10007,mo=123456789;long long i,j,k,l,t,n,m,ans,yi,er;long long f[maxn],a[maxn],g[maxn];int main(){    freopen("raviel.in","r",stdin);    freopen("raviel.out","w",stdout);    scanf("%lld",&n);    f[0]=1,g[1]=1;a[1]=1;    fo(i,2,n)f[i]=(f[i-1]+f[i-2])%mo,g[i]=(g[i-1]+g[i-2])%mo,a[i]=(a[i-1]+g[i])%mo;    n--;    fo(i,1,2*n+2){        t=0;        fo(j,0,n-i)t=(t+f[j])%mo;        ans=t*f[i]%mo;        yi=(i>n)?n:i;        er=(i-n>1)?i-n:1;        fo(j,er,yi-1)ans=(ans+f[j]*f[i-j-1]%mo*a[min(n-j,n-i+j)]%mo)%mo;        printf("%lld ",ans);    }}
1 0