pog loves szh III HDU - 5266(LCA最近公共祖先)

Pog and Szh are playing games. Firstly Pog draw a tree on the paper. Here we define 1 as the root of the tree.Then Szh choose some nodes from the tree. He wants Pog helps to find the least common ancestor (LCA) of these node.The question is too difficult for Pog.So he decided to simplify the problems.The nodes picked are consecutive numbers from lili to riri ([li,ri])([li,ri]).

Hint : You should be careful about stack overflow !
Input
Several groups of data (no more than 3 groups,n≥10000n≥10000 or Q≥10000Q≥10000).

The following line contains ans integers,n(2≤n≤300000)n(2≤n≤300000).

AT The following n−1n−1 line, two integers are bibi and cici at every line, it shows an edge connecting bibi and cici.

The following line contains ans integers,Q(Q≤300000)Q(Q≤300000).

AT The following QQ line contains two integers li and ri(1≤li≤ri≤n1≤li≤ri≤n).
Output
For each case,output QQ integers means the LCA of [li,ri][li,ri].
Sample Input
5
1 2
1 3
3 4
4 5
5
1 2
2 3
3 4
3 5
1 5
Sample Output
1
1
3
3
1





Hint

Be careful about stack overflow.

求LCA最近公共祖先有3种方法:

  1:在线算法  dfs序+线段树(或dfs序+rmq,rmq要更快,本人rmq较弱所以就用线段数将就一下)

  2:离线算法  tarjan 

  3:在线算法  树链剖分(这个是非常规求法,这篇博客就不讲了,想了解的:传送门)  

1. dfs序+线段树

  dfs深搜得到dfs序,当询问节点l和r的LCA时,只需用线段树在dfs序中找到深度最小的节点,此节点就是LCA

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;

const int inf=0x3f3f3f3f;
vector<int>vec[300005];
int dfsx[600005],dfsx2[600005],dfsn,first[300005],tree[2400005],tree2[2400005],dist[300005],vis[300005],n;///tree2[]为查询树

void dfs(int x)///求dfs序
{
    vis[x]=1;
    dfsx[++dfsn]=x;
    dfsx2[dfsn]=dist[x];
    first[x]=dfsn;
    for(int i=0;i<vec[x].size();i++){
        int v=vec[x][i];
        if(vis[v]==0){
            dist[v]=dist[x]+1;
            dfs(v);
            dfsx[++dfsn]=x;
            dfsx2[dfsn]=dist[x];
        }
    }
}

void up(int root)///线段树 合并
{
    int ll=tree[root<<1],rr=tree[root<<1|1];
    if(dfsx2[ll]>dfsx2[rr]){tree[root]=rr;return;}
    tree[root]=ll;
}

void maketree(int l,int r,int root)///线段树 建树
{
    if(l==r){tree[root]=l;return;}
    int mid=(l+r)>>1;
    maketree(l,mid,root<<1);
    maketree(mid+1,r,root<<1|1);
    up(root);
}

void up2(int root)///线段树 合并
{
    int ll=tree2[root<<1],rr=tree2[root<<1|1];
    if(dfsx2[ll]>dfsx2[rr]){tree2[root]=rr;return;}
    tree2[root]=ll;
}

void zhao(int l,int r,int root,int ll,int rr)///线段树 查找
{
    if(r<ll||l>rr){tree2[root]=0;return;}
    if(l>=ll&&r<=rr){
        tree2[root]=tree[root];
        return;
    }
    int mid=(l+r)>>1;
    zhao(l,mid,root<<1,ll,rr);
    zhao(mid+1,r,root<<1|1,ll,rr);
    up2(root);

}

void init()
{
    for(int i=1;i<=n;i++){
        vis[i]=0;
        vec[i].clear();
    }
}

int main()
{
    dfsx2[0]=inf;
    while(scanf("%d",&n)!=EOF){
        init();
        int a,b;
        for(int i=1;i<n;i++){
            scanf("%d%d",&a,&b);
            vec[a].push_back(b);
            vec[b].push_back(a);
        }
        dfsn=0;
        dist[1]=1;
        dfs(1);
        maketree(1,dfsn,1);
        int m;
        scanf("%d",&m);
        while(m--){
            scanf("%d%d",&a,&b);
            int l=first[a],r=first[b];
            if(l>r)swap(l,r);
            zhao(1,dfsn,1,l,r);
            printf("%d\n",dfsx[tree2[1]]);
        }
    }
    return 0;
}



2.离线算法 tarjan

  先将询问保存,然后dfs遍历,从子节点返回时要用并查集合并,当遍历到一个节点u时,查看所有的“询问对”u<->v,当v已经被dfs访问并标记时,此“询问对”u<->v的LCA就为fa[v]。

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=40010;

struct note
{
    int u,v,w,lca,next;
}edge[maxn*2],edge1[805];

int head[maxn],ip,head1[maxn],ip1;
int m,n;
int father[maxn],vis[maxn],ance[maxn],dir[maxn];

void init()
{
    memset(vis,0,sizeof(vis));
    memset(dir,0,sizeof(dir));
    memset(head,-1,sizeof(head));
    memset(head1,-1,sizeof(head1));
    ip=ip1=0;
}

void addedge(int u,int v,int w)
{
    edge[ip].v=v,edge[ip].w=w,edge[ip].next=head[u],head[u]=ip++;
}

void addedge1(int u,int v)
{
    edge1[ip1].u=u,edge1[ip1].v=v,edge1[ip1].lca=-1,edge1[ip1].next=head1[u],head1[u]=ip1++;
}

int  Find(int x)///路径压缩
{
    if(father[x]==x)
        return x;
    return father[x]=Find(father[x]);
}

void Union(int x,int y)///x,y合并
{
    x=Find(x);
    y=Find(y);
    if(x!=y)
        father[y]=x;
}

void tarjan(int u)
{
    vis[u]=1;
    ance[u]=father[u]=u;
    for(int i=head[u];i!=-1;i=edge[i].next)///往下深搜
    {
        int v=edge[i].v;
        int w=edge[i].w;
        if(!vis[v])
        {
           dir[v]=dir[u]+w;
           tarjan(v);
           Union(u,v);
        }
    }
    for(int i=head1[u];i!=-1;i=edge1[i].next)///
    {
        int v=edge1[i].v;
        if(vis[v])///v已经被访问并标记,可以得出此询问的结果
        {
            edge1[i].lca=edge1[i^1].lca=ance[Find(v)];///此询问对u,v和询问对v,u都会得出答案
        }
    }
}
int main()
{
   int T;
   scanf("%d",&T);
   while(T--)
   {
       init();
       scanf("%d%d",&n,&m);
       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);
       }
       for(int i=0;i<m;i++)
       {
           int u,v;
           scanf("%d%d",&u,&v);
           addedge1(u,v);
           addedge1(v,u);
       }
       dir[1]=0;
       tarjan(1);
       for(int i=0;i<m;i++)
       {
           int s=i*2,u=edge1[s].u,v=edge1[s].v,lca=edge1[s].lca;
           printf("%d\n",dir[u]+dir[v]-2*dir[lca]);
       }
   }
   return 0;
}


1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看REaDME.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 6、可私信博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值