题目描述
给出一棵n个点的树(以1号点为根),定义dep[i]为点i到根路径上点的个数。众所周知,树上最近公共祖先问题可以用倍增算法解决。现在我们需要算出这个算法精确的复杂度。我们定义计算点i和点j最近公共组先的精确复杂度为bit[dep[i]-dep[lca(i,j)]]+bitdep[j]-dep[lca(i,j)]。为了计算平均所需的复杂度为多少,请你帮忙计算任意两点计算最近公共组先所需复杂度的总和。
即计算 ∑n−1i=1∑nj=i+1 bit[dep[i]-dep[lca(i,j)]]+bit[dep[j]-dep[lca(i,j)]]
做一发
我们注意到,如果第j位有贡献,那么从i往上跳2^j,然后不能再跳超过2^j。
因此可以考虑倍增。
比较简单,详见代码
#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=100000+10;
int f[maxn][20],g[maxn][20],size[maxn],d[maxn];
int h[maxn],go[maxn*2],next[maxn*2];
ll sum[maxn][20],num[maxn][20],ans;
int i,j,k,l,t,n,m,tot;
int read(){
int x=0;
char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}
return x;
}
void add(int x,int y){
go[++tot]=y;
next[tot]=h[x];
h[x]=tot;
}
void dfs(int x,int y){
d[x]=d[y]+1;
f[x][0]=y;
int t=h[x];
size[x]=1;
while (t){
if (go[t]!=y){
dfs(go[t],x);
size[x]+=size[go[t]];
}
t=next[t];
}
}
int main(){
n=read();
fo(i,1,n-1){
j=read();k=read();
add(j,k);add(k,j);
}
dfs(1,0);
fo(j,1,floor(log(n)/log(2)))
fo(i,1,n)
f[i][j]=f[f[i][j-1]][j-1];
fo(i,1,n) g[i][0]=i;
fo(j,1,floor(log(n)/log(2)))
fo(i,1,n)
g[i][j]=f[g[i][j-1]][j-1];
fo(i,2,n) num[i][0]=sum[i][0]=size[f[i][0]]-size[i];
fo(j,1,floor(log(n)/log(2)))
fo(i,1,n){
if (!f[i][j]){
num[i][j]=num[i][j-1];
continue;
}
k=f[i][j];
sum[i][j]=num[k][j-1];
if (!g[k][j]) l=1;else l=g[k][j];
t=g[i][j];
sum[i][j]+=(ll)(size[l]-size[t]);
num[i][j]=num[i][j-1]+sum[i][j];
}
fo(j,0,floor(log(n)/log(2)))
fo(i,1,n)
ans+=sum[i][j];
printf("%lld\n",ans);
}