1.倍增法(在线做法)
首先预处理从任一节点出发依次到达节点的树深,以及到该节点的路径长度,分别用d[],dis[]表示。
f[i][j]表示i节点向着根节点走2^j步能到达的节点,若走2^j步后没有点则记为0,所以输入点时需要看题目有没有0点输入,如果有的话,给所有点都向后偏移一位。
i向根节点走2^k步可以分解为走2^(k-1)步,再走2^(k-1)步,那么可以得到递推公式:f[i][j]=f[ f[i][j-1] ][j-1];
故一次bfs预处理就可以处理出来f[][],dis[],d[]数组。
在之后寻找x与y的LCA,
- 首先选择较深的那个点,试着向根节点走,调整到与另一节点同一层次,那么这两个点要么处于同一个点,要么处以同一层次的不同点处;
- 若此时两点不同,则两点尝试同时向根节点走相同的步数,如果走相同步数后两点相同则不走,否则走
- 则最后x与y的上一层一定是最近的公共祖先了。
关于为什么第二步的走法,看图:若相同时向上走的话,会走到最顶端,而这个点不是LCA。
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=1e5+7;
int f[maxn][18];//f[i][j]表示从i向上翻2^j到达的节点;
int d[maxn];//树深;
ll dis[maxn];//该点距离1的距离;
int mi;//次幂;
struct Edge{
int v,next;
ll w;
}edge[maxn<<1];
int head[maxn],top;
void add(int u,int v,ll w){
edge[top].v=v;
edge[top].w=w;
edge[top].next=head[u];
head[u]=top++;
}
void init(){
memset(head,-1,sizeof(head));
top=0;
memset(d,0,sizeof(d));
memset(f,0,sizeof(f));
memset(dis,0,sizeof(dis));
}
queue<int>q;
void bfs(){
while(!q.empty()) q.pop();
dis[1]=0;
q.push(1);
d[1]=1;
int u,v;
while(!q.empty()){
u=q.front(); q.pop();
for(int i=head[u];i!=-1;i=edge[i].next){
v=edge[i].v;
if(d[v]) continue;
d[v]=d[u]+1;
dis[v]=dis[u]+edge[i].w;
f[v][0]=u;
for(int j=1;j<=mi;++j)
f[v][j]=f[f[v][j-1]][j-1];
q.push(v);
}
}
}
int LCA(int x,int y){
if(d[x]>d[y]) swap(x,y);//y比x深;
for(int i=mi;i>=0;--i)
if(d[f[y][i]]>=d[x]) y=f[y][i];
if(x==y) return x;
for(int i=mi;i>=0;--i)
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
return f[x][0];
}
ll getlen(int x,int y){
return dis[x]+dis[y]-2*dis[LCA(x,y)];
}
给一道题:https://codeforces.com/gym/101808/problem/K
这个题给的是个n条边的图,那么先去掉一条边变成树之后跑就可以了。
2.tarjan算法(离线做法)以How far away ? HDU - 2586为例
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=4e4+7;
const int maxm=8e4+7;
struct Edge{
int v,w,next;
}edge[maxm];
int head[maxn],top;
void init(){
top=0;
memset(head,-1,sizeof(head));
}
void add(int u,int v,int w){
edge[top].v=v;
edge[top].w=w;
edge[top].next=head[u];
head[u]=top++;
}
int fa[maxn],d[maxn],vis[maxn],lca[maxn],ans[maxn];
vector<int> query[maxn],query_id[maxn];
void add_query(int x,int y,int id){
query[x].push_back(y),query_id[x].push_back(id);
query[y].push_back(x),query_id[y].push_back(id);
}
int get(int x){
if(x==fa[x]) return x;
return fa[x]=get(fa[x]);
}
void tarjan(int u){
vis[u]=1;
int v,w,id;
for(int i=head[u];i!=-1;i=edge[i].next){
v=edge[i].v;
w=edge[i].w;
if(vis[v]) continue;
d[v]=d[u]+w;
tarjan(v);
fa[v]=u;
}
for(int i=0;i<query[u].size();++i){
v=query[u][i],id=query_id[u][i];
if(vis[v]==2){
int lca=get(v);
ans[id]=min(ans[id],d[u]+d[v]-2*d[lca]);
}
}
vis[u]=2;
}
int main(){
int t,n,m,u,v,w;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
top=0;
for(int i=1;i<=n;++i){
head[i]=-1;
fa[i]=i;
vis[i]=0;
query[i].clear(),query_id[i].clear();
}
for(int i=1;i<n;++i){
scanf("%d%d%d",&u,&v,&w);
add(u,v,w),add(v,u,w);
}
for(int i=1;i<=m;++i){
scanf("%d%d",&u,&v);
if(u==v) ans[i]=0;
else{
add_query(u,v,i);
ans[i]=1<<30;
}
}
tarjan(1);
for(int i=1;i<=m;++i) printf("%d\n",ans[i]);
if(t) printf("\n");
}
return 0;
}