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;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值