51 nod 1628 非波那契树

原题链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1628

花了一个早上+半个下午终于把这题切掉了……

神题啊(膜出题人)

我们考虑斐波那契数列的通项公式:

$$ F(i)=[(\frac{1+\sqrt{5}}{2})^{i}-(\frac{1-\sqrt{5}}{2})^{i}]*\frac{1}{\sqrt{5}} $$

最后的$ \frac{1}{\sqrt{5}} $ 我们可以拿掉,输出时再丢进答案。

然后考虑对前面两项分开单独处理。

设$G(i)=(\frac{1+\sqrt{5}}{2})^{i}$ 那么这时可以发现 $G(a+b)=G(a)*G(b)$ 有了这个性质,我们就可以把东西统计完再乘起来了。

具体来说,先预处理出从一个向上走$2^{i}$步之后,这条链上的点从下往上走能得到的贡献,从上往下相同,以及该链内部的点两两之间对答案的贡献(其实前面从上往下的统计只是为这部分服务)。

对于每组询问,先求出两点的lca,然后分别统计这两个点到lca的链的答案,再合并起来,合并的过程有很多细节,详见代码。

运气#1

#include<cstdio>
#include<algorithm>
#define MN 100001
using namespace std;
int read_p,read_ca;
inline int read(){
    read_p=0;read_ca=getchar();
    while(read_ca<‘0‘||read_ca>‘9‘) read_ca=getchar();
    while(read_ca>=‘0‘&&read_ca<=‘9‘) read_p=read_p*10+read_ca-48,read_ca=getchar();
    return read_p;
}
const int S1=691504013,S2=308495997,MOD=1e9+9,S3=276601605;
struct na{int y,ne;}b[MN<<1];
int n,m,x,y,f[MN][21],g[MN][21],G[MN][21],A[MN][21],K[MN][21],l[MN],num=0,S[2][MN],X[MN],Y[MN],Z[MN],s[MN],de[MN],mmh[MN],Lx[MN],Ly[MN],AP[MN];
inline void in(int x,int y){b[++num].y=y;b[num].ne=l[x];l[x]=num;}
inline int M(int x){while (x>=MOD)x-=MOD;while (x<0)x+=MOD;return x;}
void DFS(int x){
    s[x]=1;
    register int i;
    for (i=1;i<=20;i++) if (f[f[x][i-1]][i-1]) f[x][i]=f[f[x][i-1]][i-1];else break;
    for (i=1;i<=20;i++) if (f[K[x][i-1]][i]) K[x][i]=f[K[x][i-1]][i];else break;
    for (;i;i--) K[x][i]=K[x][i-1];K[x][0]=x;
    for (i=l[x];i;i=b[i].ne)
    if (b[i].y!=f[x][0]){
        de[b[i].y]=de[K[b[i].y][0]=f[b[i].y][0]=x]+1;
        DFS(b[i].y);
        AP[x]=M(AP[x]+1LL*s[x]*s[b[i].y]%MOD);
        s[x]+=s[b[i].y];
    }
}
inline int lca(int x,int y,int &a,int &b){
    for (register int i=20;i>=0;i--)
    if (de[f[x][i]]>=de[y]) x=f[x][i];
    if (x==y) return x;
    for (register int i=20;i>=0;i--)
    if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    a=x;b=y;
    return f[x][0];
}
void dfs(int x,int o){
    A[x][0]=1LL*AP[x]*S[o][1]%MOD;
    g[x][0]=G[x][0]=1LL*s[x]*S[o][1]%MOD;
    for (register int i=1;i<=20;i++)
    if (K[x][i])
    g[x][i]=M((1LL*(g[x][i-1]-s[K[x][i-1]])*S[o][1<<(i-1)]+g[f[x][i-1]][i-1])%MOD),
    G[x][i]=M((1LL*(G[f[x][i-1]][i-1]-g[K[x][i-1]][0])*S[o][1<<(i-1)]+G[x][i-1])%MOD),
    A[x][i]=M((1LL*g[x][i-1]*(G[f[x][i-1]][i-1]-g[K[x][i-1]][0])+A[x][i-1]+A[f[x][i-1]][i-1]-1LL*s[K[x][i-1]]*M(G[f[x][i-1]][i-1]-g[K[x][i-1]][0]))%MOD);
    for (register int i=l[x];i;i=b[i].ne)
    if (b[i].y!=f[x][0]) dfs(b[i].y,o);
}
inline void work(int x,int z,int &_A,int &_g,int o){
    int p=0;_A=0;_g=0;
    for (register int i=20;i>=0;i--)
    if (de[K[x][i]]>=de[z]){
        _A=M((1LL*_g*(G[x][i]-g[p][0])+_A+A[x][i]-1LL*s[p]*M(G[x][i]-g[p][0]))%MOD);
        _g=M((1LL*(_g-s[p])*S[o][1<<i]+g[x][i])%MOD);
        if (K[x][i]==z) return;
        p=K[x][i];
        x=f[x][i];
    }
}
inline void calc(int x){
    int Ax,Ay,gx,gy;
    register int i;
    dfs(1,x);
    for (i=1;i<=m;i++)
    if (Y[i]==Z[i]){
        work(X[i],Z[i],Ax,gx,x);
        mmh[i]=M(mmh[i]+(x?-1LL:1LL)*(1LL*gx*(n-s[Z[i]])+Ax)%MOD);
    }else{
        work(X[i],Lx[i],Ax,gx,x);work(Y[i],Ly[i],Ay,gy,x);
        mmh[i]=M(mmh[i]+(x?-1LL:1LL)*(1LL*(gx+gy)*(n-s[Lx[i]]-s[Ly[i]])%MOD*S[x][1]+1LL*gx*gy%MOD*S[x][1]+Ax+Ay+A[Z[i]][0]-(1LL*s[Lx[i]]*(s[Z[i]]-s[Lx[i]])+1LL*s[Ly[i]]*(s[Z[i]]-s[Lx[i]]-s[Ly[i]]))%MOD*S[x][1]+(1LL*(s[Z[i]]-s[Lx[i]]-s[Ly[i]])*(n-s[Z[i]]))%MOD*S[x][1])%MOD);
    }
}
int main(){
    register int i;
    n=read();
    for (i=1;i<n;i++) x=read(),y=read(),in(x,y),in(y,x);
    S[0][0]=S[1][0]=1;
    for (i=1;i<=n;i++) S[0][i]=1LL*S[0][i-1]*S1%MOD,S[1][i]=1LL*S[1][i-1]*S2%MOD;
    de[1]=1;DFS(1);
    m=read();
    for (i=1;i<=m;i++){
        X[i]=read();Y[i]=read();
        if (de[X[i]]<de[Y[i]]) swap(X[i],Y[i]);
        Z[i]=lca(X[i],Y[i],Lx[i],Ly[i]);
    }
    calc(0);calc(1);
    for (i=1;i<=m;i++) printf("%d\n",1LL*mmh[i]*S3%MOD);
}

时间: 2024-11-05 16:39:36

51 nod 1628 非波那契树的相关文章

51 nod 1350 斐波那契表示

每一个正整数都可以表示为若干个斐波那契数的和,一个整数可能存在多种不同的表示方法,例如:14 = 13 + 1 = 8 + 5 + 1,其中13 + 1是最短的表示(只用了2个斐波那契数).定义F(n) = n的最短表示中的数字个数,F(14) = 2,F(100) = 3(100 = 3 + 8 + 89),F(16) = 2(16 = 8 + 8 = 13 + 3).定义G(n) = F(1) + F(2) + F(3) + ...... F(n),G(6) = 1 + 1 + 1 + 2

51 nod 1427 文明 (并查集 + 树的直径)

1427 文明 题目来源: CodeForces 基准时间限制:1.5 秒 空间限制:131072 KB 分值: 160 难度:6级算法题 安德鲁在玩一个叫“文明”的游戏.大妈正在帮助他. 这个游戏里面有n个城市和m条双向的道路.城市从1到n编号.对于每一对城市,他们之间要么有唯一的一条道路,要么就是不可互达.一条道路的定义是一个包含不同城市的序列 v1, v2,...,vk ,  vi  和  vi+1 (1≤ i < k)之间有直接的一条道路相连.这条道路的长度是k-1.两个城市在同一区域的

斐波那契数列深入学习

问题定义: 具体文字定义就不多说了,网上有很多,下面给出数学公式描述(有时候会有一些变种,不过都大同小异): f(0) = 1 , f(1) = 1, f(n) = f(n-1)+f(n-2) n>=2 1.递归求解 解决斐波那契问题,大多数人第一反应就是递归,思路简单清晰,代码易实现,不多说了,直接看代码. 1 long long int Fibonacci(int n) 2 { 3 if (n < 2) { 4 return 1; 5 } 6 7 return Fibonacci(n-1)

java程序员到底该不该了解一点算法(一个简单的递归计算斐波那契数列的案例说明算法对程序的重要性)

为什么说 “算法是程序的灵魂这句话一点也不为过”,请看下面这个简单的案例 1 package recursion; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 6 import org.junit.Test; 7 8 /** 9 * @author: MengXianman 10 * @creationTime: 2017年11月27日 上午9:47:51 11 * @description: 斐波那契数列结合备忘录算法的简单使

用递归和非递归的方法输出斐波那契数列的第n个元素(C语言实现)

费波那契数列(意大利语:Successione di Fibonacci),又译为费波拿契数.斐波那契数列.费氏数列.黄金分割数列. 在数学上,费波那契数列是以递归的方法来定义: {\displaystyle F_{0}=0} {\displaystyle F_{1}=1} {\displaystyle F_{n}=F_{n-1}+F_{n-2}}(n≧2) 用文字来说,就是费波那契数列由0和1开始,之后的费波那契系数就是由之前的两数相加而得出.首几个费波那契系数是: 0, 1, 1, 2, 3

斐波那契数与二分法的递归与非递归算法及其复杂度分析

1. 什么是斐波那契数? 这里我借用百度百科上的解释:斐波那契数,亦称之为斐波那契数列(意大利语: Successione di Fibonacci),又称黄金分割数列.费波那西数列.费波拿契数.费氏数列,指的是这样一个数列:0.1.1.2.3.5.8.13.21.--在数学上,斐波纳契数列以如下被以递归的方法定义:F0=0,F1=1,Fn=Fn-1+Fn-2(n>=2,n∈N*),用文字来说,就是斐波那契数列列由 0 和 1 开始,之后的斐波那契数列系数就由之前的两数相加.特别指出:0不是第一

Benelux Algorithm Programming Contest 2014 Final ACM-ICPC Asia Training League 暑假第一阶段第二场 E. Excellent Engineers-单点更新、区间最值-线段树 G. Growling Gears I. Interesting Integers-类似斐波那契数列-递推思维题

先写这几道题,比赛的时候有事就只签了个到. E. Excellent Engineers 传送门: 这个题的意思就是如果一个人的r1,r2,r3中的某一个比已存在的人中的小,就把这个人添加到名单中. 因为是3个变量,所以按其中一个变量进行sort排序,然后,剩下的两个变量,一个当位置pos,一个当值val,通过线段树的单点更新和区间最值操作,就可以把名单确定. 代码: 1 //E-线段树 2 #include<iostream> 3 #include<cstdio> 4 #incl

简述java递归与非递归算法,0-100求和,斐波那契数列,八皇后,汉诺塔问题

一:什么是递归算法? 递归算法就是直接或者间接的调用自己的方法,在达到一个条件的时候停止调用(递归出口),所以一定要找准好条件,让递归停止,否则就会是无限进行下去 二:递归程序设计的关键 1:找出调用中所需要的参数 2:返回的结果 3:递归调用结束的条件 三:递归程序注意 1:要有方法中自己调用自己 2:要有分支结构 3:要有结束的条件 四:简单叙述递归函数的优缺点 优点: 1:简洁清晰,实现容易,可读性好 2:在遍历的算法中,递归比循环更为简单 缺点: 1:效率低,使用递归函数是有空间和时间的

51 nod 1766 树上的最远点对(线段树+lca)

1766 树上的最远点对 基准时间限制:3 秒 空间限制:524288 KB 分值: 80 难度:5级算法题 n个点被n-1条边连接成了一颗树,给出a~b和c~d两个区间,表示点的标号请你求出两个区间内各选一点之间的最大距离,即你需要求出max{dis(i,j) |a<=i<=b,c<=j<=d} (PS 建议使用读入优化) Input 第一行一个数字 n n<=100000. 第二行到第n行每行三个数字描述路的情况, x,y,z (1<=x,y<=n,1<