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

来源:互联网 发布:三星刷windows phone 编辑:程序博客网 时间:2024/06/06 02:05

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(0i<n)的点有k^i个,所有深度≠n-1的点都有k个孩子。YJC发现他不会做了,于是他来问你这个问题的答案。这个答案可能很大,你只需要告诉他答案%998244353的值就可以了。

Solution

我们设f[i]表示i层k叉数的数量,则f[i]=ki1k1那么我们发现ans=n1i=0(f[ni+1]2kf[ni]2)iki,代入得ans=n1i=0(kni+11k12kkni1k12)iki化简一下就得到ans=n1i=0ikiik2ni+11k,ans=n1i=0ikiik2ni+11k我们知道n1i=0iki=nn1i=0kini=1i1j=0kj,我们将ik2ni+1视为ik2n+1(1k)i所以将式子代入得k2nk(2n1)kn(k1)(k1)3。所以算法复杂度为O(logN).

Code

#include<iostream>#include<cmath>#include<cstring>#include<cstdio>#include<algorithm>#define ll long longusing namespace std;const ll maxn=1000005,mo=998244353;ll f[maxn],n,i,t,j,k,l,m,ans,x;ll sqr(ll x){    return x*x%mo;}int main(){    freopen("lca.in","r",stdin);freopen("lca.out","w",stdout);    scanf("%lld%lld",&n,&k);    f[1]=1;ans=0;n--;    for (i=2;i<=n+1;i++)        f[i]=(f[i-1]*k+1)%mo;    x=1;    for (i=0;i<=n;i++){        t=x*((sqr(f[n-i+1])-k*sqr(f[n-i])%mo+mo)%mo)%mo;        ans=(ans+t*i%mo)%mo;        x=x*k%mo;    }    printf("%lld\n",ans);}
1 0
原创粉丝点击