LCA

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,

  1. 首先选择较深的那个点,试着向根节点走,调整到与另一节点同一层次,那么这两个点要么处于同一个点,要么处以同一层次的不同点处;
  2. 若此时两点不同,则两点尝试同时向根节点走相同的步数,如果走相同步数后两点相同则不走,否则走
  3. 则最后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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值