您的位置:首页 > 其它

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

2016-12-12 20:44 501 查看
-##Description

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

Solution

我们设f[i]表示第i层有多少白色节点,g1[i]表示第i层有多少黑色节点,g[i]表示前i层有多少黑色节点。那么显然f[i]和g1[i]都是满足斐波那契数列的形态,所以可以线性求一下,再求一下g[i]。现在分类讨论一下。我们会发现,其实所谓求的f[i]即一个白色点往下走i步后遇到的白色节点数。i从零开始标号,n-1结尾。

1、当前白色节点与它子树的白色节点距离为i。那么显然有f[i]∑n−ij=0f[j]。

2、当存在两个白色节点的lca为黑色节点时,我们设一个白色端点到黑色端点的距离为k,那么就有ans=∑i−1k=0f[k+1]∗f[i−k+1]∗g[min(n−i+k,n−k)]。但这样会出现重复情况,我们发现一个f[i]=f[i-1]+f[i-2]其实就是一个白色点由一个黑色点下方的白点和黑点转移得到,所以我们将ans=∑i−1k=0f[k]∗f[i−k−1]∗g[min(n−i+k,n−k)],就没有重复情况了。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const ll mo=123456789,maxn=5005,ni=61728395;
ll f[maxn],g[maxn],g1[maxn];
ll n,i,t,j,k,l,p,ans,q,z;
int main(){
freopen("raviel.in","r",stdin);freopen("raviel.out","w",stdout);
scanf("%lld",&n);f[0]=1;g1[1]=1;g[1]=1;
for (i=2;i<=n;i++)
f[i]=(f[i-1]+f[i-2])%mo,g1[i]=(g1[i-1]+g1[i-2])%mo,g[i]=(g[i-1]+g1[i])%mo;
l=2*n;n--;
for (i=1;i<=l;i++){
t=0;
for (j=0;j<=n-i;j++)
t=(t+f[j])%mo;
ans=t*f[i]%mo;
if (i>n) p=n;
else p=i;
if (i-n>1) q=i-n;
else q=1;
for (k=q;k<p;k++)
ans=(ans+f[k]*f[i-k-1]%mo*g[min(n-k,n-i+k)]%mo)%mo;
printf("%lld ",ans);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: