先抛出一个问题,一棵n个点的树,每个点有一个不变的权值,m次询问任意两点之间的权值和。
最简单的算法:m次循环,每次从一个点出发,dfs累计走过的路径,直到到达另外一个点。
int dfs(int x,int fa,long long deep)
{
d[x]=deep;
for(int i=head[x];i;i=nxt[i])
{
int u=to[i];if(u==fa) continue;
dfs(u,x,deep+w[i]);
}
}
如果你会前缀和,将其利用到树上,将一条条链想象成数组,于是乎我们就可以快速求出在一条链上的两点之间的权值和。
对于不在一条链上的点怎么办呢?我们求一下两点(a,b)的LCA,将(a,b)分为(lca,a)和(lca,b),这样lca和每一个点都在一条链上了。
void get_treesum(int x,int fa)
{
sum[x]=sum[fa]+a[x];
for(int i=head[x];i;i=nxt[i])
{
int u=to[i];if(u==fa) continue;
get_treesum(u,x);
}
}
long long get_ans(int x,int y,int lca)
{
return sum[x]+sum[y]-sum[lca]-sum[fa[lca]];
}
如果还需要你支持一种操作:修改任意一个点的权值
我们还是先考虑处理的是数组,对于数组,我们不能再使用前缀和了,否则每次修改都是o(n)的,我们就可以使用线段树或者树状