By pjx,拿走请附上https://www.cnblogs.com/pjxpjx/p/12431613.html
Update 2020.3.16 修改复杂度写错问题
Update 2020.8.2 添加代码注释
定义
顾名思义,这个东西就是求树上任意两个点 \(u,v\) 的最短距离。
解法(以边权,无向边为例)
1.暴力
随便找一个点为根,把边权挂到儿子节点上,对于每一对 \(u,v\) ,先使用倍增法求出 \(LCA(u,v)\) ,再暴力从 \(u\) 开始向上一步一步跳至 \(LCA(u,v)\) ,每跳一次就加上那条边的权值;再从 \(LCA(u,v)\) 向下一步一步跳至 \(v\), 每跳一次就加上那条边的权值。
复杂度:\(O(n^2)\)
代码太暴力了,就不给了
2.前缀和优化
暴力的算法太暴力了(?),我们可以用前缀和优化一下。
在学数组一维前缀和时,求一段数之和是这样写的:
cout<<total[v]-total[u-1];//sum表示预处理前缀和的数组
所以,把这个式子拓展到树上,就可以求出一条链上任意两点的前缀和了。
这个只能处理呈一条链的前缀和,对不是链数据难道就无能为力了?
不是!
通过暴力可以看出,求距离可以分成两段,\(u到LCA(u,v)\) 和 \(LCA(u,v)到v\)。
所以,我们把求距离砍成两段,最后综合一下就可以了。
先预处理出前缀和数组,然后就按照一维前缀和的样子来做:
ans+=total[u]-total[LCA(u,v)];
ans+=total[v]-total[LCA(u,v)];
最后综合并成一个式子:
cout<<total[u]+total[v]-2*total[LCA(u,v)];
来张图更易理解:
如图,\(LCA(5,3)=4\),所以答案就是\(total[5]+total[3]-2*total[4]\)
这个算法更加优化,复杂度少了许多。
复杂度:\(O(nlogn)\)
code:
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <queue>
using namespace std;
const int N=200005;
const int M=25;
int Q,n,m,head[N],fa[N][M],cnt,dep[N],b[N],k,total[N],wei[N];
struct node{
int u,v,next,sum;
}ed[N];
void add_edge(int u,int v,int k) //邻接表存图,不懂的搜索一下
{
cnt++;
ed[cnt].u=u;
ed[cnt].v=v;
ed[cnt].next=head[u];
ed[cnt].sum=k;
head[u]=cnt;
}
void dfs(int xx)
{
for(int i=1;i<=20;i++)
{
fa[xx][i]=fa[fa[xx][i-1]][i-1];//使用倍增思路
}
for(int i=head[xx];i!=0;i=ed[i].next)//枚举
{
int temp=ed[i].v;
if(b[temp]==0)
{
b[temp]=1;
dep[temp]=dep[xx]+1;
total[temp]=ed[i].sum+total[xx];//累加前缀和
fa[temp][0]=xx;
dfs(temp);
}
}
}
int LCA(int x,int y)
{
if(dep[x]<dep[y])
swap(x,y);
int temp=dep[x]-dep[y];
for(int i=20;i>=0;i--)
{
if(1<<i&temp)
{
x=fa[x][i];
}
}
if(x==y)
return x;
for(int i=20;i>=0;i--)
{
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];
}
int main()
{
cin>>n;
cin>>m;
for(int i=1;i<n;i++)
{
int x,y,k;
scanf("%d%d%d",&x,&y,&k);
add_edge(x,y,k);
add_edge(y,x,k);
}
b[1]=1;
dep[1]=1;//注意开始的初始值
dfs(1);
while(m--)
{
int x,y;
scanf("%d%d",&x,&y);
int lca=LCA(x,y);
cout<<total[x]+total[y]-total[lca]-total[lca]<<endl; // 根据公式
}
return 0;
}
模板题目:
By pjx