【NOIP2016提高A组集训第14场11.12】最近公共祖先

75 篇文章 0 订阅

Description

YJC最近在学习树的有关知识。今天,他遇到了这么一个概念:最近公共祖先。对于有根树T的两个结点u、v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u、v的祖先且x的深度尽可能大。YJC很聪明,他很快就学会了如何求最近公共祖先。他现在想寻找最近公共祖先有什么性质,于是他提出了这样的一个问题:n层的满k叉树T,求对于每一对(i,j)(1≤i,j≤T的点数),LCA(T,i,j)的深度的和是多少。这个数字n层的满k叉树指一棵带标号的有根树,深度为i(0≤i

Solution

这道题一看到就开始推 O(n) 怎么做。
考虑点对的lca贡献为i的点对数= ki(size[ni+1]2ksize[ni]2) ,因为这层一共有 ki 个节点,然后单个节点的里面所有节点先两两匹配,然后还要减去他们k个儿子额两两匹配。

然后答案= n1i=0iki(size[ni+1]2ksize[ni]2)
因为size是节点个数,可以用等比数列来求出来: size[i]=1+k+k2+......+ki=ki+11k1
那么带入上面的式子就可以变成 n1i=0i(k2ni+1ki)k1
然后答案= 1k1(n1i=0ik2ni+1n1i=0iki)
= 1k1(k2n+1n1i=0in1i=0ikin1i=0iki)

现在我们的主要问题就是 n1i=0iki=??????
我们的目的只是要把 i ki分离出来。
那么考虑给每个 ki 都补上一个系数 n ,那么上式就变成nn1i=0kini=1i1j=0kj

那么把这个代入上式,那么答案就变成:
1k1(k2n+1(n1)nnn1i=0kini=1i1j=0kjnn1i=0ki+ni=1i1j=0kj

然后再把等比数列求和套进去:
答案就可以化简成:
k2nk(2n1)kn(k1)(k1)3

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const ll maxn=1000007,mo=998244353;
ll i,j,k,l,t,n,m,ans,yi,er;
ll ci[maxn],size[maxn];
ll qsm(ll x,ll y){
    ll z=1;
    for(;y;y/=2,x=x*x%mo)if(y&1)z=z*x%mo;
    return z;
}
int main(){
    freopen("lca.in","r",stdin);
    freopen("lca.out","w",stdout);
    scanf("%lld%lld",&n,&k);
    ans=(qsm(k,2*n)-k-(2*n-1)*qsm(k,n)%mo*(k-1)%mo)%mo;
    ans=(ans+mo)%mo;
    t=qsm(k-1,3);
    ans=ans*qsm(t,mo-2)%mo;
    printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值