LCA-离线tarjan模板

/*
 *算法引入:
 *树上两点的最近公共祖先;
 *对于有根树的两个结点u,v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u,v的祖先且x的深度尽可能大;
 *对于x来说,从u到v的路径一定经过点x;
 *
 *算法思想:
 *Tarjan_LCA离线算法;
 *Tarjan算法基于dfs的框架,对于新搜到的一个结点,首先创建由这个结点构成的集合,再对当前结点的每个子树进行搜索;
 *每搜索完一棵子树,则可确定子树内的LCA询问都已解决,其他的LCA询问的结果必然在这个子树之外;
 *这时把子树所形成的集合与当前结点的集合合并,并将当前结点设为这个集合的祖先;
 *之后继续搜索下一棵子树,直到当前结点的所有子树搜完;
 *
 *这时把当前结点也设为已被检查过的,同时可以处理有关当前结点的LCA询问;
 *如果有一个从当前结点到结点v的询问,且v已经被检查过;
 *则由于进行的是dfs,当前结点与v的最近公共祖先一定还没有被检查;
 *而这个最近公共祖先的包含v的子树一定已经搜索过了,那么这个最近公共祖先一定是v所在集合的祖先;
 *
 *算法步骤:
 *对于每一个结点:
 *(1)建立以u为代表元素的集合;
 *(2)遍历与u相连的结点v,如果没有被访问过,对于v使用Tarjan_LCA算法,结束后将v的集合并入u的集合;
 *(3)对于与u有关的询问(u,v),如果v被访问过,则结果就是v所在集合的代表元素;
 *
 *算法示例:
 *HDU2586(How far away?)
 *
 *题目大意:
 *求树上任两点间的距离;
 *
 *算法思想:
 *先dfs一遍,求出到根节点的dis;
 *对于某个询问,求u和v的lca,然后res[i]=d[u]+d[v]-2*d[lca(u,v)];
 *
**/
struct node{
    int to,w,next,lca;
};

int fa[maxn];
int head[maxn*2];
int qhead[maxm];//询问
bool vis[maxn];
ll d[maxn];
ll res[maxm];

node edge[N*2];
node qedge[M];//询问边

int n,m;
int cnt1,cnt2;

int findFa(int x){
    if(fa[x]!=x) fa[x]=findFa(fa[x]);
    return fa[x];
}

inline void Addedge(int u,int v,int w){
    edge[++cnt1].w=w;
    edge[cnt1].to=v;
    edge[cnt1].next=head[u];
    head[u]=cnt1;

    edge[++cnt1].w=w;
    edge[cnt1].to=u;
    edge[cnt1].next=head[v];
    head[v]=cnt1;
}

inline void Addqedge(int u,int v){
    qedge[++cnt2].to=v;
    qedge[cnt2].next=qhead[u];
    qhead[u]=cnt2;

    /*
    qedge[++cnt2].to=u;
    qedge[cnt2].next=qhead[v];
    qhead[v]=cnt2;
    */
}

void dfs(int u,int fa,ll w){
    d[u]=w;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(v==fa)
            continue;
        dfs(v,u,w+edge[i].w);
    }
}

void Tarjan_LCA(int u){ //离线LCA算法
    fa[u]=u;
    vis[u]=true;
    for(int i=head[u];i!=-1;i=edge[i].next){
        if(!vis[edge[i].to]){
            Tarjan_LCA(edge[i].to);
            fa[edge[i].to]=u;
        }
    }

    for(int i=qhead[u];i!=-1;i=qedge[i].next){
        if(vis[qedge[i].to]){
            qedge[i].lca=findFa(qedge[i].to);
            res[i]=d[u]+d[qedge[i].to]-2*d[qedge[i].lca]; //两者距离
        }
    }
}

void solve()
{
    rep(i,0,n) fa[i]=i;
    mem(head,-1);  mem(qhead,-1);  mem(vis,false);
    cnt1=cnt2=0;
    int u,v,w;
    rep(i,1,n-1){
        scanf("%d%d%d",&u,&v,&w);
        Addedge(u,v,w);
    }
    while(m--){
        scanf("%d%d",&u,&v);
        Addqedge(u,v);
    }
    dfs(1,-1,0);
    Tarjan_LCA(1);
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        solve();
        rep(i,1,m)
            printf("%lld\n",res[i]);
    }
}
时间: 2024-11-10 00:09:17

LCA-离线tarjan模板的相关文章

[POJ1330]Nearest Common Ancestors(LCA, 离线tarjan)

题目链接:http://poj.org/problem?id=1330 题意就是求一组最近公共祖先,昨晚学了离线tarjan,今天来实现一下. 个人感觉tarjan算法是利用了dfs序和节点深度的关系,大致的意思:dfs如果不递归到递归基,那么dfs就会越递归越深,这个时候深度也是相应增加的,所以这个时候任意在已经遍历过的节点中选取两个点,计算他们的lca也就相当于是用并查集求他们的root.而dfs执行到递归基,转而执行下一个分支的时候,这个时候dfs的节点应当是小于等于之前执行到递归基的节点

LCA(最近公共祖先)——离线 Tarjan 算法

一.梳理概念 定义:对于有根树T的两个结点u.v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u.v的祖先且x的深度尽可能大. 通俗地讲,最近公共祖先节点,就是两个节点在这棵树上深度最大的公共的祖先节点,即两个点在这棵树上距离最近的公共祖先节点. 提示:父亲节点也是祖先节点,节点本身也是它的祖先节点. 给出一棵树,如图所示: 由上面的定义可知:3和5的最近公共祖先为1,5和6的最近公共祖先为2,2和7的最近公共祖先为2, 6和7的最近公共祖先为4. 二.繁文缛节 注意注意注意!!!尚

POJ 1330 Nearest Common Ancestors LCA(在线RMQ,离线Tarjan)

链接:http://poj.org/problem?id=1330 题意:只看题目就知道题目是什么意思了,最近公共祖先,求在一棵树上两个节点的最近公共祖先. 思路:求最近公共祖先有两种算法,在线和离线,在线方法是用RMQ求LCA,一句话总结就是在从DFS时,从第一个点到第二个点的最短路径中深度最浅的点就是公共祖先,用RMQ处理,一般问题的最优解决方式的复杂度是O(NlogN)的预处理+N*O(1)的查询.离线方法是Tarjan算法,将所有询问的两个点都记录下来,在DFS过程中不断将每个点自身作为

POJ 1330 LCA最近公共祖先 离线tarjan算法

题意要求一棵树上,两个点的最近公共祖先 即LCA 现学了一下LCA-Tarjan算法,还挺好理解的,这是个离线的算法,先把询问存贮起来,在一遍dfs过程中,找到了对应的询问点,即可输出 原理用了并查集和dfs染色,先dfs到底层开始往上回溯,边并查集合并 一边染色,这样只要询问的两个点均被染色了,就可以输出当前并查集的最高父亲一定是LCA,因为我是从底层层层往上DSU和染色的,要么没被染色,被染色之后,肯定就是当前节点是最近的 #include <iostream> #include <

POJ - 1470 Closest Common Ancestors(离线Tarjan算法)

1.输出测试用例中是最近公共祖先的节点,以及这个节点作为最近公共祖先的次数. 2.最近公共祖先,离线Tarjan算法 3. /* POJ 1470 给出一颗有向树,Q个查询 输出查询结果中每个点出现次数 */ /* 离线算法,LCATarjan 复杂度O(n+Q); */ #include<iostream> #include<stdio.h> #include<string.h> using namespace std; const int MAXN=1010; co

HDU 2874 LCA离线算法

Connections between cities Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4477    Accepted Submission(s): 1284 Problem Description After World War X, a lot of cities have been seriously damage

学渣乱搞系列之Tarjan模板合集

学渣乱搞系列之Tarjan模板合集 by 狂徒归来 一.求强连通子图 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #include <climits> 7 #include <vector> 8 #include <queue> 9 #incl

HDU5156(离线tarjan+打标记)

Harry and Christmas tree Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 213    Accepted Submission(s): 53 Problem Description In Christmas Day, Harry got a Christmas tree. The tree has n nodes

tarjan模板(缩点,求有向图强连通分量)

具体思路见详解网址:https://www.byvoid.com/blog/scc-tarjan: 然后整理出了这个tarjan模板,具体数组的功能代码都有注释. const int N=100010; struct data { int to,next; } tu[N*2]; int head[N]; int ip; int dfn[N], low[N];///dfn[]表示深搜的步数,low[u]表示u或u的子树能够追溯到的最早的栈中节点的次序号 int sccno[N];///缩点数组,表

图论算法-Tarjan模板 【缩点;割顶;双连通分量】

图论算法-Tarjan模板 [缩点:割顶:双连通分量] 为小伙伴们总结的Tarjan三大算法 Tarjan缩点(求强连通分量) int n; int low[100010],dfn[100010]; bool ins[100010]; int col[100010];//记录每个点所属强连通分量(即染色) vector<int> map[100010]; stack<int> st; int tot;//时间戳 int colnum;//记录强连通分量个数 void tarjan(