LCA的五种解法

标准求法

//O(nlogn)-O(logn)
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100010;
int first[maxn],next[maxn*2],to[maxn*2],dis[maxn*2];
int n,m;
void AddEdge(int a,int b,int c)
{
     to[++m]=b;
     dis[m]=c;
     next[m]=first[a];
     first[a]=m;
}
int dep[maxn],fa[maxn],cost[maxn],anc[20][maxn];
void dfs(int x)
{
    dep[x]=dep[fa[x]]+1;
    for(int i=first[x];i;i=next[i])
    {
       if(to[i]!=fa[x])
       {
           fa[to[i]]=x;
           cost[to[i]]=cost[x]+dis[i];
           dfs(to[i]);
       }
    }
}
void preprocess()
{
    for(int i=1;i<=n;i++) anc[0][i]=fa[i];
    for(int j=1;(1<<j)<=n;j++)
       for(int i=1;i<=n;i++)
          if(anc[j-1][i])
          {
              int a=anc[j-1][i];
              anc[j][i]=anc[j-1][a];
          }
}
int LCA(int p,int q)
{
    if(dep[p]<dep[q]) swap(p,q);
    int log=1;
    for(;(1<<log)<=dep[p];log++); log--;
    for(int i=log;i>=0;i--) if(dep[p]-dep[q]>=(1<<i)) p=anc[i][p];
    if(p==q) return p;
    for(int i=log;i>=0;i--)
       if(anc[i][p]!=anc[i][q])
       {
           p=anc[i][p];
           q=anc[i][q];
       }
    return fa[p];
}
int main()
{
    int a,b,c,Q;
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        AddEdge(a,b,c);
        AddEdge(b,a,c);
    }
    scanf("%d",&Q);
    dfs(1);
    preprocess();
    while(Q--)
    {
        scanf("%d%d",&a,&b);
        c=LCA(a,b);
        printf("%d %d\n",dep[a]+dep[b]-dep[c]*2+1,cost[a]+cost[b]-cost[c]*2);
    }
    return 0;
}

欧拉序列套区间最小值

//O(nlogn)-O(1)
#include<iostream>
#include<cstdio>
using namespace std;
const int maxn=100010;
int n,e=1;
int first[maxn],next[maxn*2],dis[maxn*2],to[maxn*2];
void AddEdge(int a,int b,int c)
{
    to[e]=b;
    dis[e]=c;
    next[e]=first[a];
    first[a]=e++;
}
int fa[maxn],dep[maxn],d[maxn],p[maxn],A[maxn*2],z=1;
void dfs(int x)
{
    dep[x]=dep[fa[x]]+1;
    p[x]=z; A[z++]=x;
    for(int i=first[x];i;i=next[i])
    {
        if(fa[x]!=to[i])
        {
            d[to[i]]=d[x]+dis[i];
            fa[to[i]]=x;
            dfs(to[i]);
            A[z++]=x;
        }
    }
}
int mv[20][maxn*2],len[maxn*2];
void RMQ_init(int m)
{
    for(int i=1;i<=m;i++) mv[0][i]=A[i];
    for(int j=1;(1<<j)<=m;j++)
       for(int i=1;i+(1<<j)<=m+1;i++)
       {
           mv[j][i]=mv[j-1][i];
           if(dep[mv[j-1][i]]>dep[mv[j-1][i+(1<<(j-1))]]) mv[j][i]=mv[j-1][i+(1<<(j-1))];
       }
    for(int i=1;i<=m;i++)
       while(1<<(len[i]+1)<=i) len[i]++;
}
int LCA(int a,int b)
{
    if(a<b) swap(a,b);
    int k=len[a-b+1];
    return dep[mv[k][b]]<dep[mv[k][a-(1<<k)+1]]?mv[k][b]:mv[k][a-(1<<k)+1];
}
int main()
{
    int a,b,c,Q;
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        AddEdge(a,b,c);
        AddEdge(b,a,c);
    }
    dfs(1);
    RMQ_init(z-1);
    scanf("%d",&Q);
    while(Q--)
    {
        scanf("%d%d",&a,&b);
        c=LCA(p[a],p[b]);
        printf("%d %d\n",dep[a]+dep[b]-2*dep[c]+1,d[a]+d[b]-2*d[c]);
    }
    return 0;
} 

树链剖分

//O(n)-O(logn)
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100010;
struct Edge
{
    int b,c;
}es[maxn*2];
int n,m=1;
int first[maxn],next[maxn*2];
void AddEdge(int a,int b,int c)
{
    es[m]=(Edge){b,c};
    next[m]=first[a];
    first[a]=m++;
}
int dep[maxn],siz[maxn],son[maxn],fa[maxn],dis[maxn];
void dfs(int x)
{
    siz[x]=1; dep[x]=dep[fa[x]]+1;
    for(int i=first[x];i;i=next[i])
    {
       Edge& e=es[i];
       if(e.b!=fa[x])
       {
          fa[e.b]=x;
          dis[e.b]=dis[x]+e.c;
          dfs(e.b);
          siz[x]+=siz[e.b];
          if(siz[son[x]]<siz[e.b]) son[x]=e.b;
       }
    }
}
int top[maxn];
void build(int x)
{
    if(son[x])
    {
       top[son[x]]=top[x];
       build(son[x]);
    }
    for(int i=first[x];i;i=next[i])
    {
       Edge& e=es[i];
       if(e.b!=fa[x]&&e.b!=son[x])
       {
           top[e.b]=e.b;
           build(e.b);
       }
    }
}
int query(int va,int vb)
{
     int f1=top[va],f2=top[vb];
     while(f1!=f2)
     {
         if(dep[f1]<dep[f2])
         {
            swap(f1,f2);
            swap(va,vb);
         }
         va=fa[f1]; f1=top[va];
     }
     if(dep[va]>dep[vb]) swap(va,vb);
     return va;
}
int main()
{
    int a,b,c,Q;
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
       scanf("%d%d%d",&a,&b,&c);
       AddEdge(a,b,c);
       AddEdge(b,a,c);
    }
    dfs(1);
    top[1]=1;
    build(1);
    scanf("%d",&Q);
    while(Q--)
    {
        scanf("%d%d",&a,&b);
        c=query(a,b);
        printf("%d %d\n",dep[a]+dep[b]-2*dep[c]+1,dis[a]+dis[b]-2*dis[c]);
    }
    return 0;
}

Tarjan算法(离线)

//O(n)-O(1)
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=100010;
int n,m;
int first[maxn],next[maxn*2],to[maxn*2],d[maxn*2];
void AddEdge(int a,int b,int c)
{
    to[++m]=b;
    d[m]=c;
    next[m]=first[a];
    first[a]=m;
}
int f2[maxn],n2[maxn*2],v[maxn*2],id[maxn*2];
void AddQuery(int a,int b,int c)
{
    v[++m]=b;
    id[m]=c;
    n2[m]=f2[a];
    f2[a]=m;
}
int vis[maxn],dis[maxn],dep[maxn],f[maxn],ans[maxn];
int findset(int x) {return x==f[x]?x:f[x]=findset(f[x]);}
void dfs(int x,int fa)
{
    f[x]=x; dep[x]=dep[fa]+1;
    for(int i=first[x];i;i=next[i])
    {
        if(to[i]!=fa)
        {
            dis[to[i]]=dis[x]+d[i];
            dfs(to[i],x);
            f[to[i]]=f[x];
        }
    }
    vis[x]=1;
    for(int i=f2[x];i;i=n2[i]) if(vis[v[i]]) ans[id[i]]=findset(v[i]);
}
int A[maxn],B[maxn];
int main()
{
    scanf("%d",&n);
    int a,b,c,Q;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        AddEdge(a,b,c);
        AddEdge(b,a,c);
    }
    scanf("%d",&Q);
    m=0;
    for(int i=0;i<Q;i++)
    {
        scanf("%d%d",&A[i],&B[i]);
        AddQuery(A[i],B[i],i);
        AddQuery(B[i],A[i],i);
    }
    dfs(1,0);
    for(int i=0;i<Q;i++) printf("%d %d\n",dep[A[i]]+dep[B[i]]-dep[ans[i]]*2+1,dis[A[i]]+dis[B[i]]-dis[ans[i]]*2);
    return 0;
}

LCT

//O(n)-O(logn)
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn=100010;
int n,m;
int first[maxn],next[maxn*2],to[maxn*2],d[maxn*2];
void AddEdge(int a,int b,int c)
{
    to[++m]=b;
    d[m]=c;
    next[m]=first[a];
    first[a]=m;
}
int ans1,ans2;
struct LCT
{
    int pre[maxn],ch[maxn][2],fa[maxn],v[maxn],sumv[maxn],s[maxn];
    void init()
    {
        memset(pre,0,sizeof(pre));
        memset(ch,0,sizeof(ch));
        s[0]=fa[1]=v[0]=sumv[0]=0;
    }
    void maintain(int x)
    {
        sumv[x]=v[x]+sumv[ch[x][0]]+sumv[ch[x][1]];
        s[x]=s[ch[x][0]]+s[ch[x][1]]+1;
    }
    void rotate(int x,int d)
    {
        int y=pre[x],z=pre[y];
        ch[y][d^1]=ch[x][d];
        pre[ch[x][d]]=y;
        ch[z][ch[z][1]==y]=x;
        pre[x]=z;
        ch[x][d]=y;
        pre[y]=x;
        maintain(y);
    }
    void splay(int x)
    {
        int rt=x;
        while(pre[rt]) rt=pre[rt];
        if(rt!=x)
        {
            fa[x]=fa[rt];
            fa[rt]=0;
            while(pre[x]) rotate(x,ch[pre[x]][0]==x);
        }
        maintain(x);
    }
    void access(int x)
    {
        int y=0;
        for(;x;x=fa[x])
        {
            splay(x);
            fa[ch[x][1]]=x;
            pre[ch[x][1]]=0;
            ch[x][1]=y;
            fa[y]=0;
            pre[y]=x;
            y=x;
            maintain(x);
        }
    }
    void query(int x,int y)
    {
        access(y);
        for(y=0;x;x=fa[x])
        {
            splay(x);
            if(!fa[x])
            {
                ans1=s[ch[x][1]]+s[y]+1;
                ans2=sumv[ch[x][1]]+sumv[y];
                return;
            }
            fa[ch[x][1]]=x;
            pre[ch[x][1]]=0;
            ch[x][1]=y;
            fa[y]=0;
            pre[y]=x;
            y=x;
            maintain(x);
        }
    }
}sol;
int vis[maxn];
queue<int> Q;
void BFS(int x)
{
    vis[1]=1; Q.push(1);
    while(!Q.empty())
    {
        x=Q.front(); Q.pop();
        for(int i=first[x];i;i=next[i])
            if(!vis[to[i]])
            {
                vis[to[i]]=1;
                sol.fa[to[i]]=x;
                sol.v[to[i]]=d[i];
                Q.push(to[i]);
                sol.maintain(to[i]);
            }
    }
}
int main()
{
    scanf("%d",&n);
    int a,b,c,Q;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        AddEdge(a,b,c);
        AddEdge(b,a,c);
    }
    sol.init();
    BFS(1);
    scanf("%d",&Q);
    while(Q--)
    {
        scanf("%d%d",&a,&b);
        sol.query(a,b);
        printf("%d %d\n",ans1,ans2);
    }
    return 0;
}

时间: 2024-10-26 00:12:15

LCA的五种解法的相关文章

LeetCode算法题-Find the Difference(Java实现-五种解法)

这是悦乐书的第214次更新,第227篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第82题(顺位题号是389).给定两个字符串s和t,它们只包含小写字母.字符串t由随机混洗字符串s生成,然后在随机位置再添加一个字母.找到t中添加的字母.例如: 输入:s ="abcd", t ="abcde" 输出:'e' 说明:'e'是添加的字母. 本次解题使用的开发工具是eclipse,jdk使用的版本是1.8,环境是win7 64位系统,使用Jav

算法题的五种解法

方法一:举例法 先列举一些具体的例子,看能否发现其中的一般规则. 示例:给定一个具体时间,计算时针与分针之间的角度. 方法二:模式匹配法 将现有问题与相似问题作类比,看看内否通过修改相关问题的解法来解决新问题. 分析:这个问题和两个问题有点类似 1)在一个无序的数组中查找最小的元素 这个问题就是遍历所有元素即可,没有用到给定信息(这是一个有序数组),实在没办法再用这种. 2)在一个有序的数组中找出某个特定元素(比如通过二分查找法) 这是一个有序数组,只是一部分元素循环移动过.因此元素排序肯定是从

Longest Common Prefix 五种解法(JAVA)

解法一:水平扫描 int indexOf(String str): 在字符串中检索str,返回其第一出现的位置,如果找不到则返回-1 class Solution { public String longestCommonPrefix(String[] strs) { if(strs.length == 0) return ""; String prefix = strs[0]; for(int i = 1; i < strs.length; i++){ while(strs[i

凸包问题的五种解法[转]

前言: 首先,什么是凸包? 假设平面上有p0~p12共13个点,过某些点作一个多边形,使这个多边形能把所有点都“包”起来.当这个多边形是凸多边形的时候,我们就叫它“凸包”.如下图: 然后,什么是凸包问题? 我们把这些点放在二维坐标系里面,那么每个点都能用 (x,y) 来表示. 现给出点的数目13,和各个点的坐标.求构成凸包的点? 解一:穷举法(蛮力法) 时间复杂度:O(n³). 思路:两点确定一条直线,如果剩余的其它点都在这条直线的同一侧,则这两个点是凸包上的点,否则就不是. 步骤: 将点集里面

16 级高代 II 思考题九的七种解法

16 级高代 II 思考题九  设 $V$ 是数域 $\mathbb{K}$ 上的 $n$ 维线性空间, $\varphi$ 是 $V$ 上的线性变换, $f(\lambda),m(\lambda)$ 分别是 $\varphi$ 的特征多项式和极小多项式. 设 $f(\lambda)=m(\lambda)=P_1(\lambda)^{r_1}P_2(\lambda)^{r_2}\cdots P_k(\lambda)^{r_k}$, 其中 $P_1(\lambda),P_2(\lambda),\c

LeetCode算法题-Minimum Distance Between BST Nodes(Java实现-四种解法)

这是悦乐书的第314次更新,第335篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第183题(顺位题号是783).给定具有根节点值的二叉搜索树(BST),返回树中任何两个不同节点的值之间的最小差值.示例: 给定的树[4,2,6,1,3,null,null]由下图表示: 4 / 2 6 / \ 1 3 输出:1 说明:请注意,root是TreeNode对象,而不是数组.该树中的任意节点最小差值为1,它发生在节点1和节点2之间,也发生在节点3和节点2之间. 注意: BS

Meteor 中的代码包有点特殊,分为五种

Meteor 中的代码包有点特殊,分为五种: Meteor 核心代码本身分成多个核心代码包(core package),每个 Meteor 应用中都包含,你基本上不需要花费精力来维护它们 常规 Meteor 代码包称为"isopack",或同构代码包(isomorphic package,意味着它们既能在客户端也能在服务器端工作).第一类代码包例如 accounts-ui 或 appcache 由 Meteor 核心团队维护,与 Meteor 捆绑在一起. 第三方代码包就是其他用户开发

五种方式让你在java中读取properties文件内容不再是难题

一.背景 最近,在项目开发的过程中,遇到需要在properties文件中定义一些自定义的变量,以供java程序动态的读取,修改变量,不再需要修改代码的问题.就借此机会把Spring+SpringMVC+Mybatis整合开发的项目中通过java程序读取properties文件内容的方式进行了梳理和分析,先和大家共享. 二.项目环境介绍 Spring 4.2.6.RELEASE SpringMvc 4.2.6.RELEASE Mybatis 3.2.8 Maven 3.3.9 Jdk 1.7 Id

Unix网络编程中的五种I/O模型_转

转自:Unix网络编程中的的五种I/O模型 下面主要是把unp第六章介绍的五种I/O模型. 1. 阻塞I/O模型 例如UDP函数recvfrom的内核到应用层.应用层到内核的调用过程是这样的:首先把描述符.接受数据缓冲地址.大小传递给内核,但是如果此时 该与该套接口相应的缓冲区没有数据,这个时候就recvfrom就会卡(阻塞)在这里,知道数据到来的时候,再把数据拷贝到应用层,也就是传进来的地址空 间,如果没有数据到来,就会使该函数阻塞在那里,这就叫做阻塞I/O模型,如下图: 2. 非阻塞I/O模