关于LCA几个讲解:
https://blog.csdn.net/jeryjeryjery/article/details/52853017
1.How far away ?
http://acm.hdu.edu.cn/showproblem.php?pid=2586
题目大意:给出n个房屋,这些房屋之间的链接是树形链接,房屋和房屋之间有且只有一个路线;道路是双向的。
T组数据;
n个房子,m次查询;
n-1个路径 :a,b之间的距离是l
倍增:
倍增法讲解:https://blog.csdn.net/lw277232240/article/details/72870644
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int MAX=1e6+5;
const int INF=0x3f3f3f3f;
struct node{
int t,next;
ll len;
//Node(int T=0,int Next=-1,ll Len=0):t(T),next(Next),len(Len){}
}edge[MAX<<1];
int head[MAX],cnt;
void initedge(int n)
{
memset(head,-1,sizeof(head));cnt=0;
}
void addedge(int u,int v,ll len=0)
{
edge[cnt]=node{v,head[u],len};
//这样写不加 Node(int T=0,int Next=-1,ll Len=0):t(T),next(Next),len(Len){} 最好用G++提交
head[u]=cnt++;
}
int T,n,m;
int deep[MAX];
int anc[202020][21];
int dis[202020];
void dfs(int u,int father)
{
for(int i=1;i<=20;i++)
{
anc[u][i]=anc[ anc[u][i-1] ][i-1];//找u的2的i次方的父节点
}
for(int i=head[u];~i;i=edge[i].next)
{
int t=edge[i].t;
if(t==father)continue;
//因为这里建立的是双向图!!!addedge(u,v,w); addedge(v,u,w);两张方向相反的图
dis[t]=dis[u]+edge[i].len;
deep[t]=deep[u]+1;
anc[t][0]=u;
dfs(t,u);
}
}
///查询
int LCA(int u,int v)
{
if(deep[u]<deep[v])swap(u,v); ///1.调整u深
for(int i=20;i>=0;i--) ///2.调整到统一深度
{
if(deep[anc[u][i]] >= deep[v])
u=anc[u][i];
}
if(u==v)return u;
for(int i=20;i>=0;i--) ///3.一起往上跳
{
if(anc[u][i] != anc[v][i])
{
u=anc[u][i];
v=anc[v][i];
}
}
return anc[u][0];
}
int main()
{
for(scanf("%d",&T);T--;)
{
scanf("%d%d",&n,&m);
initedge(n);
for(int i=1;i<n;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
dis[1]=0;
deep[1]=1;
anc[1][0]=1;
dfs(1,1);
for(int i=0;i<m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
int k=LCA(u,v);
int ans=dis[u]+dis[v]-2*dis[k];
printf("%d\n",ans);
}
}
}
tarjan:
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#define ll long long
const int maxn=1e5+10;
using namespace std;
int rlen[maxn];
int vis[maxn];
int fa[maxn];
struct node
{
int u,v,w,next;
}edge[maxn];
struct query
{
int u,v,lca,next;
}edgeq[maxn*4];
int head[maxn],cnt;
int headq[maxn],cntq;
void addedge(int u,int v,int len)
{
edge[cnt]=node{u,v,len,head[u]};
head[u]=cnt++;
}
void addq(int u,int v){ //询问
edgeq[cntq]=query{u,v,0,headq[u]};
headq[u]=cntq++;
}
void initedge()
{
cnt=0;
memset(head,-1,sizeof(head));
}
void initq()
{
cntq=0;
memset(headq,-1,sizeof(headq));
}
int Find(int u)
{
if(u==fa[u])
return u;
return fa[u]=Find(fa[u]);
}
void Union(int x,int y)
{
x=Find(x);
y=Find(y);
if(x!=y)
fa[y]=x;
}
void dfs(int u)//注意理解递归的过程!
{
vis[u]=1;
fa[u]=u;
//通过递归更新子节点到根节点的距离和更新子节的祖节点的位置
for(int i=head[u];~i;i=edge[i].next)
{
int t=edge[i].v;
if(!vis[t])
{
rlen[t]=rlen[u]+edge[i].w;
dfs(t);
Union(u,t);//遍历完到最底层后,回溯时再更新父节点!!1
}
}
//每遍历完一个父节点的所有子节点,就与父节点相关的最近LCA的询问
for(int i=headq[u];~i;i=edgeq[i].next)
{
int v=edgeq[i].v;
if(vis[v])//重点!如果该子节点已经被访问过,那么父节点与该子节点的最近LCA一定就是子节点的祖节点
{
int lca=Find(v);
edgeq[i].lca=lca;
}
}
}
int main()
{
int cas,n,m,u,v,len;
cin>>cas;
while(cas--)
{
scanf("%d%d",&n,&m);
initedge();
initq();
for(int i=0;i<n-1;i++){
scanf("%d%d%d",&u,&v,&len);
addedge(u,v,len);
addedge(v,u,len);
}
///输入询问 u v
for(int i=0;i<m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
addq(u,v);
addq(v,u);
}
memset(vis,0,sizeof(vis));
rlen[1]=0;
dfs(1); //LCA
for(int i=0;i<m;i++)
{
//应为是双向图,所以要乘以2
int ans=rlen[edgeq[2*i].u]+rlen[edgeq[2*i].v]-2*rlen[edgeq[2*i].lca];
printf("%d\n",ans);
}
}
}
2.Design the city
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3195
题意:
给定N个点,下面N-1行u、v、d表示一条无向边和边权值,它们构成了一棵无向树。现在给出Q查询。
问在树上连通a、b、c三点的最短距离。
显然在树上连通a、b、c三点最短距离 为dist[a] + dist[b] + dist[c] - (dist[LCA(a, b)] + dist[LCA(a, c)] + dist[LCA(b, c)])。其中dist[]存储节点到根的最短距离。
答案也就是
(lca(a,b)+lca(b,c)+lca(a,c))/2
lca(a,b)=dist[a] + dist[b] -- 2*dist[lca(a, b)]
注意输出格式!!!!
倍增:
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#define ll long long
const int MAX=1e5+10;
using namespace std;
struct node{
int t,next;
ll len;
}edge[MAX<<1];
int head[MAX],cnt;
void initedge()
{
memset(head,-1,sizeof(head));
cnt=0;
}
void addedge(int u,int v,ll len=0)
{
edge[cnt]=node{v,head[u],len};
head[u]=cnt++;
}
int T,n,m;
int deep[MAX];
int anc[202020][21];
int dis[202020];
void dfs(int u,int father)
{
for(int i=1;i<=20;i++)
{
anc[u][i]=anc[ anc[u][i-1] ][i-1];//找u的2的i次方的父节点
}
for(int i=head[u];~i;i=edge[i].next)
{
int t=edge[i].t;
if(t==father)continue;
dis[t]=dis[u]+edge[i].len;
deep[t]=deep[u]+1;
anc[t][0]=u;
dfs(t,u);
}
}
///查询
int LCA(int u,int v)
{
if(deep[u]<deep[v])swap(u,v); ///1.调整u深
for(int i=20;i>=0;i--) ///2.调整到统一深度
{
if(deep[anc[u][i]] >= deep[v])
u=anc[u][i];
}
if(u==v)return u;
for(int i=20;i>=0;i--) ///3.一起往上跳
{
if(anc[u][i] != anc[v][i])
{
u=anc[u][i];
v=anc[v][i];
}
}
return anc[u][0];
}
int jisuan(int u,int v)
{
int ans=(dis[u]+dis[v]-2*dis[LCA(u,v)]);
return ans;
}
int main()
{
int flag=0;//注意输出格式!!!
while(~scanf("%d",&n))
{
//注意输出格式!!!
if(flag)
printf("\n");
flag=1;
initedge();
for(int i=1;i<n;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
addedge(u,v,w);
addedge(v,u,w);
}
dis[1]=0;
deep[1]=1;
anc[1][0]=1;
dfs(1,1);
scanf("%d",&m);
for(int i=0;i<m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
int sum=jisuan(u,v)+jisuan(u,w)+jisuan(v,w);
printf("%d\n",sum/2);
}
}
}
tarjan(WA了,但不知道错在哪了啊?):
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <math.h>
#define ll long long
using namespace std;
const int nmaxn=50000+10;
const int qmaxn=70000+10;
ll rlen[qmaxn];
int vis[qmaxn];
int fa[qmaxn];
struct node
{
int u,v;
ll w;
int next;
node (int U=0,int V=0,ll W=0,int N=0):
u(U),v(V),w(W),next(N){}
}edge[nmaxn<<1];// 注意数组开的大小!
struct query
{
int u,v,lca,next;
}edgeq[qmaxn*6];// 注意数组开的大小!
int head[nmaxn],cnt;
int headq[qmaxn],cntq;
void addedge(int u,int v,ll len)
{
edge[cnt]=node(u,v,len,head[u]);
head[u]=cnt++;
}
void addq(int u,int v)
{
edgeq[cntq]=query{u,v,-1,headq[u]};
headq[u]=cntq++;
}
void initedge()
{
cnt=0;
memset(head,-1,sizeof(head));
}
void initq()
{
cntq=0;
memset(headq,-1,sizeof(headq));
}
int Find(int u)
{
if(u==fa[u])
return u;
return fa[u]=Find(fa[u]);
}
void Union(int x,int y)
{
x=Find(x);
y=Find(y);
if(x!=y)
fa[y]=x;
}
void dfs(int u)
{
vis[u]=1;
fa[u]=u;
for(int i=head[u];~i;i=edge[i].next)
{
int t=edge[i].v;
if(!vis[t])
{
rlen[t]=rlen[u]+edge[i].w;// 注意这里!
dfs(t);
Union(u,t);
}
}
for(int i=headq[u];~i;i=edgeq[i].next)
{
int v=edgeq[i].v;
if(vis[v])// 注意这里已标记过的
{
int lca=Find(v);
edgeq[i].lca=lca;
}
}
}
ll jisuan(int x)
{
return rlen[edgeq[x].u]+rlen[edgeq[x].v]-2*rlen[edgeq[x].lca];
}
int main()
{
int flag,n,m,u,v,w;
ll len;
flag=0;
while(~scanf("%d",&n))
{
if(flag)
printf("\n");
flag=1;
initedge();
initq();
memset(vis,0,sizeof(vis));
memset(fa,0,sizeof(fa));
memset(rlen,0,sizeof(rlen));
for(int i=0;i<n-1;i++)
{
scanf("%d%d%lld",&u,&v,&len);
addedge(u,v,len);
addedge(v,u,len);
}
scanf("%d",&m);
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&u,&v,&w);
addq(u,v);
//addq(v,u);
addq(u,w);
//addq(w,u);
addq(v,w);
//addq(w,v);
}
dfs(0); //LCA
for(int i=0;i<m;i++)
{
int a=i*3,b=a+1,c=a+2;//注意这里与开的树大小的关系!
ll ans=jisuan(a)+jisuan(b)+jisuan(c);
printf("%lld\n",ans/2);
}
}
}