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

Description

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

Input

一行一个正整数n。

Output

一行2n个整数表示答案,对123456789取模。

Sample Input

5

Sample Output

0 2 3 3 1 1 0 0 0 0

Data Constraint

对于20%的数据n<=10;
对于40%的数据n<=20;
对于60%的数据n<=30;
对于80%的数据n<=400;
对于100%的数据n<=5000。

The Solution

我们可以先把树给画出来,然后就可以发现一个特殊的性质。


  • deep | 1 | 2 | 3 | 4 | 5|
  • black | 0 | 1 | 1 | 2 | 3|
  • white | 1 | 0 | 1 | 1 | 2|

将就将就着吧。。。不怎么会画表。。。

我们发现每层的黑点数满足斐波那契,
每层的白点从第二层开始满足斐波那契数列。

我们分成两种情况进行讨论。

首先先定几个数组
white表示白
black表示黑
pre表示1~i层的总共白点数
ans为答案。

1.
白点对的lca为其中的一个白点。我们只需要枚举距离,计算答案即可。
Ans[i]=pre[ni]white[i+1]
(pre[n-i]表示1-(n-i)层总共的白点数,这些白点都可以再向下延伸i个长度,
白点下面连接的是一颗形如从第二层黑点开始的子树,
所以是white[i+1]而不是white[i])
2.
白点对的lca为黑点。这样如果直接枚举两个深度的白点个数相乘是不对的,为什么?
因为你所枚举的两个白点有可能在同一条路径上,这样就不合法了。

所以我们考虑两条路径从lca黑点的黑白两个儿子中各取一个。

那么原本我们要求两条长度为i,j的路径合成一个i+j的路径,
就变成了从黑儿子中选一条长度i-1的路径,
从白儿子中选一条长度j-1的路径。
他的黑儿子是一颗形如从第二层黑点开始的子树,所以个数为white[i+1],
而白儿子就是从第一层直接开始的路径,
也就是white[i]实际表示的是与lca黑点相距i+1的白点,所以个数是white[j].

简单式子可以写成如下
Ans[i+j]=(Ans[i+j]+(ll)(Pre[nmax(i,j)+1]1)(ll)White[i]

CODE

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#define fo(i,a,b) for (int i=a;i<=b;i++)

using namespace std;

typedef long long ll;

const int N = 5010,mo = 123456789;

int Black[N],Pre[N],White[N],n;
ll Ans[N * 2];

int main()
{
    //freopen("raviel.in","r",stdin);
    freopen("raviel.out","w",stdout);
    scanf("%d",&n);
    Black[1] = 1,Black[2] = 1;
    fo(i,3,N-5) Black[i] = (Black[i-1] + Black[i-2]) % mo;
    White[1] = 1,White[2] = 0,White[3] = 1,White[4] = 1;
    fo(i,5,N-5) White[i] = (White[i-1] + White[i-2]) % mo;  
    fo(i,1,N-5) Pre[i] = (Pre[i-1] + (ll)White[i]) % mo;
    fo(i,1,n-1) Ans[i] = (Pre[n-i]*(ll)White[i+1]) % mo;
    fo(i,1,n-1)
        fo(j,1,n-1) Ans[i+j] = (Ans[i+j] + (ll)(Pre[n-max(i,j)+1] - 1) * (ll)White[i] % mo * (ll)White[j+1] % mo) % mo;
    fo(i,1,n*2) printf("%lld ",Ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值