LCA 最近公共祖先

https://blog.csdn.net/lwt36/article/details/50599983

 

How far away ?

HDU - 2586

There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this "How far is it if I want to go from house A to house B"? Usually it hard to answer. But luckily int this village the answer is always unique, since the roads are built in the way that there is a unique simple path("simple" means you can't visit a place twice) between every two houses. Yout task is to answer all these curious people.

Input

First line is a single integer T(T<=10), indicating the number of test cases.
  For each test case,in the first line there are two numbers n(2<=n<=40000) and m (1<=m<=200),the number of houses and the number of queries. The following n-1 lines each consisting three numbers i,j,k, separated bu a single space, meaning that there is a road connecting house i and house j,with length k(0<k<=40000).The houses are labeled from 1 to n.
  Next m lines each has distinct integers i and j, you areato answer the distance between house i and house j.

Output

For each test case,output m lines. Each line represents the answer of the query. Output a bland line after each test case.

Sample Input

2
3 2
1 2 10
3 1 15
1 2
2 3

2 2
1 2 100
1 2
2 1

Sample Output

10
25
100
100
#include <bits/stdc++.h>

using namespace std;

const int MAXN = 40005;

pair<int,int> P[205];//存询问边
int lca[205];//存询问边对应的最近公共祖先

struct Edge //存树边
{
    int to,next,w;//w为边权
} E[MAXN*2];

int head[MAXN],cut1;

inline void Add(int from,int to,int w)
{
    E[cut1].next = head[from];
    E[cut1].to = to;
    E[cut1].w = w;
    head[from] = cut1++;
}

struct Query //存每个点对应的询问边
{
    int to,next,id;
} Q[405];

int headQ[MAXN],cut2;

inline void AddQ(int from,int to,int id)
{
    Q[cut2].next = headQ[from];
    Q[cut2].to = to;
    Q[cut2].id = id;
    headQ[from] = cut2++;
}

int dis[MAXN];//记录根节点到每个点的距离
bool vis[MAXN];
int fa[MAXN];//并查集祖先数组

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

void Union(int a,int b)
{
    int A = Find(a);
    int B = Find(b);
    if(A != B)fa[B] = A;
}

void Tarjan(int rt)
{
    vis[rt] = true;
    int to;
    for(int i=head[rt] ; i!=-1 ; i=E[i].next)
    {
        to = E[i].to;
        if(!vis[to])
        {
            dis[to] = dis[rt] + E[i].w;
            Tarjan(to);
            Union(rt,to);
        }
    }
    for(int i=headQ[rt] ; i!=-1 ; i=Q[i].next)
    {
        to = Q[i].to;
        if(vis[to])
        {
            lca[Q[i].id] = Find(to);
        }
    }
}



int main()
{

    int T,n,m;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&m);
        for(int i=0 ; i<=n ; i++)
          fa[i] = i;
        memset(vis,false,sizeof vis);
        memset(head,-1,sizeof head);
        memset(headQ,-1,sizeof headQ);
        cut1 = cut2 = 0;


        int a,b,c;

        for(int i=1 ; i<n ; i++)
        {
            scanf("%d %d %d",&a,&b,&c);
            Add(a,b,c);
            Add(b,a,c);
        }
        for(int i=1 ; i<=m ; ++i)
        {
            scanf("%d %d",&P[i].first,&P[i].second);
            AddQ(P[i].first,P[i].second,i);
            AddQ(P[i].second,P[i].first,i);
        }
//        dis[1]=0;
//            Tarjan(1); //优化

        if(n!=1)
        {
            dis[n/2] = 0;
            Tarjan(n/2);
        }
        else
        {
            dis[1]=0;
            Tarjan(1);
        }

        for(int i=1 ; i<=m ; ++i)
        {
            printf("%d\n",dis[P[i].first]+dis[P[i].second]-2*dis[lca[i]]);
        }
    }

    return 0;
}

 

Connections between cities

HDU - 2874

After World War X, a lot of cities have been seriously damaged, and we need to rebuild those cities. However, some materials needed can only be produced in certain places. So we need to transport these materials from city to city. For most of roads had been totally destroyed during the war, there might be no path between two cities, no circle exists as well.
Now, your task comes. After giving you the condition of the roads, we want to know if there exists a path between any two cities. If the answer is yes, output the shortest path between them.

Input

Input consists of multiple problem instances.For each instance, first line contains three integers n, m and c, 2<=n<=10000, 0<=m<10000, 1<=c<=1000000. n represents the number of cities numbered from 1 to n. Following m lines, each line has three integers i, j and k, represent a road between city i and city j, with length k. Last c lines, two integers i, j each line, indicates a query of city i and city j.

Output

For each problem instance, one line for each query. If no path between two cities, output “Not connected”, otherwise output the length of the shortest path between them.

Sample Input

5 3 2
1 3 2
2 4 3
5 2 3
1 4
4 5

Sample Output

Not connected
6
#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn=10010, maxq=500005;
struct node1
{
    int to, next;
    ll w;
} e[maxn*2], q[maxq*2];
int cut1, head[maxn*2];
int cut2, h[maxq*2];
bool vis[maxn];
int id[maxn];//存询问边对应的最近公共祖先
int tot;
int fa[maxn];
ll dis[maxn];
ll ans[maxq];//答案
int find(int x)
{
    if(fa[x]==x)
        return x;
    return fa[x]=find(fa[x]);
}
void merge(int u, int v)
{
    int x=find(u);
    int y=find(v);
    if(x!=y)
        fa[y]=x;
}
void add_edge(int u, int v, ll w)
{
    e[cut1].to=v;
    e[cut1].next=head[u];
    e[cut1].w=w;
    head[u]=cut1++;
}
void add_query(int u, int v, int w)
{
    q[cut2].to=v;
    q[cut2].next=h[u];
    q[cut2].w=w;
    h[u]=cut2++;
}
void LCA(int u)
{
    vis[u]=1;
    id[u]=tot;
    for(int i=head[u]; i!=-1; i=e[i].next)
    {
        int v=e[i].to;
        if(vis[v])
            continue;
        dis[v]=dis[u]+e[i].w;
        LCA(v);
        merge(u, v);
    }
    for(int i=h[u]; i!=-1; i=q[i].next)
    {
        int v=q[i].to;
        if(vis[v]&&id[u]==id[v])
        {
            int an=find(v);
            int id=q[i].w;
            ans[id]=dis[u]+dis[v]-(ll)2*dis[an];
        }
    }
}
int main()
{
    int n, m, k;
    while(scanf("%d%d%d", &n, &m, &k)!=EOF)
    {
        memset(head, -1, sizeof(head));
        int u, v;
        ll w;
        cut=0;
        for(int i=0; i<m; i++)
        {
            scanf("%d%d%lld", &u, &v, &w);
            add_edge(u, v, w);
            add_edge(v, u, w);
        }
        int kt=0;
        while(k>0)
        {
            if(k>500000)
                kt=500000;
            else
                kt=k;
            k-=kt; //这可以看看

            for(int i=1; i<=n; i++)
                fa[i]=i;
            memset(h, -1, sizeof(h));
            memset(vis, 0, sizeof(vis));
            memset(id, -1, sizeof(id));
            memset(dis, 0, sizeof(dis));
            tot=0;
            cut2=0;
            memset(ans, -1, sizeof(ans));
            for(int i=0; i<kt; i++)
            {
                scanf("%d%d", &u, &v);
                add_query(u, v, i);
                add_query(v, u, i);
            }
            for(int i=1; i<=n; i++)
            {
                if(!vis[i])
                {
                    dis[i]=0;
                    tot++;
                    LCA(i);
                }
            }
            for(int i=0; i<kt; i++)
            {
                if(ans[i]==-1)
                    printf("Not connected\n");
                else
                    printf("%lld\n", ans[i]);
            }
        }
    }
    return 0;
}

 

敌对势力

终于,小 J 在车技大赛中打败小 D,成为了新一代车神。这次胜利为他赢得了极高的名誉和地位,因此经常有人请他到世界各地演讲,分享自己人生的经验。

这次,小 J 受邀来到了 A 国。这个国家一共有 NNN 个城市,城市之间被道路连接,形成一棵无根树。小 J 一共计划演讲 MMM 天,第 iii 天会在 aia_iai​ 号城市与 bib_ibi​ 号城市之间的路径上巡回演讲。然而,总有一些邪恶的敌对势力不欢迎小 J 的到来,想要在小 J 的演讲过程中捣乱。邪恶势力的头目小 L 打算在小 J 演讲的期间发起攻势,他在第 iii 天会把自己的邪恶组织成员安排在 cic_ici​ 号城市到 did_idi​ 号城市的路径上。小 J 不希望自己的演讲被扰乱,请你告诉小 J 每天的行程是否安全,也就是说,是否不存在某个城市同时位于小 J 和小 L 计划的路径当中。

输入格式

输入第一行包含两个正整数 N,MN,MN,M。接下来 N−1N-1N−1 行中,每行包含两个正整数 ui,viu_i,v_iui​,vi​,表示存在一条边连接 ui,viu_i,v_iui​,vi​。接下来MMM行中,每行包含四个正整数 ai,bi,ci,dia_i,b_i,c_i,d_iai​,bi​,ci​,di​,表示第 iii 天中小 J 和小 L 选择的路径。

输出格式

输出包含 MMM 行,表示小 J 每天的行程是否安全。如安全输出"YES",否则输出"NO"(输出不含引号)。

数据规模

对于 40% 的数据:N,M≤5000N,M\leq5000N,M≤5000;

对于另 30% 的数据:ai=bi=1a_i=b_i=1ai​=bi​=1;

对于 100% 的数据:1≤N,M≤105,1≤ui,vi,ai,bi,ci,di≤N1\leq N,M\leq 10^5,1\leq u_i,v_i,a_i,b_i,c_i,d_i\leq N1≤N,M≤105,1≤ui​,vi​,ai​,bi​,ci​,di​≤N。

输出时每行末尾的多余空格,不影响答案正确性

要求使用「文件输入输出」的方式解题,输入文件为 enemy.in,输出文件为 enemy.out

样例输入

5 4
1 2
1 3
3 4
3 5
1 2 4 5
1 3 4 5
1 5 4 4
1 5 3 4

样例输出

YES
NO
YES
NO
#include<iostream>
#include<cstdio>
#include<queue>
#include<cmath>
#include<cstring>
using namespace std;
int Next[200100],head[200100],ver[200100],size=0,d[100100],f[100100][30],t;
queue <int> q;
void add(int x,int y)//邻接表尝龟操作
{
    size++;
    Next[size]=head[x];
    head[x]=size;
    ver[size]=y;
    return ;
}
int lca(int x,int y)//倍增lca查询
{
    if(d[x]>d[y]) swap(x,y);
    for(int i=t; i>=0; i--)
    if(d[f[y][i]]>=d[x]) y=f[y][i];
    if(x==y) return x;
    for(int i=t; i>=0; i--)
    {
        if(f[x][i]!=f[y][i])
        {
            x=f[x][i];
            y=f[y][i];
        }
    }
    return f[x][0];
}
void bfs()//预处理一波
{
    q.push(1);
    d[1]=1;
    while(q.size())
    {
        int x=q.front();
        q.pop();
        for(int i=head[x]; i; i=Next[i])
        {
            int y=ver[i];
            if(d[y]) continue;
            d[y]=d[x]+1;
            f[y][0]=x;
            for(int j=1; j<=t; j++) f[y][j]=f[f[y][j-1]][j-1];
            q.push(y);
        }
    }
    return ;
}
int main()
{
    int n,m;
    memset(d,0,sizeof(d));
    scanf("%d %d",&n,&m);
    t=(int)((log(n)/log(2))+1);
    for(int i=1; i<n; i++)
    {
        int u,v;
        scanf("%d %d",&u,&v);
        add(u,v);
        add(v,u);
    }
    bfs();
    for(int i=1; i<=m; i++)
    {
        int a,b,c,e,fa1,fa2;
        scanf("%d %d %d %d",&a,&b,&c,&e);
        fa1=lca(a,b);
        fa2=lca(c,e);
        if(d[fa2]<d[fa1])//稍稍减个枝
        {
            if(lca(fa1,c)==fa1 || lca(fa1,e)==fa1)//有碰到为真
                printf("NO \n");
            else printf("YES \n");
        }
        else
        {
            if(lca(fa2,a)==fa2 || lca(fa2,b)==fa2)//有碰到为真
                printf("NO \n");
            else printf("YES \n");
        }
    }
    return 0;
}

 

L - Subway Lines

Gym - 101908L

The subway system of a major city is formed by a set of stations and tunnels that connect some pairs of stations. The system was designed so that there is exactly one sequence of tunnels linking any pair of stations. The stations for which there is only one tunnel passing through are called terminals. There are several train lines that make round trips between pairs of terminal stations, passing only through the stations in the unique path between them. People are complaining about the current lines. Therefore, the mayor ordered that the lines are redefined from scratch. As the system has many stations, we need to help the engineers, who are trying to decide which pair of terminals will define a line.

The figure illustrates a system where terminal stations are shown as filled circles and the non-terminals are shown as empty circles. On the leftmost picture, if the pair (A,B)

define a line and the pair (C,D) defines another, they won't have any common station. But, on the rightmost picture, we can see that if the pairs (E,F) and (G,H)

are chosen, those two lines will have two stations in common.

Given the description of the system and a sequence of Q

queries consisting of two pairs of terminals, your program should compute, for each query, how many stations the two lines defined by those pairs have in common.

Input

The first line of the input contains two integers N

(5≤N≤105) and Q (1≤Q≤20000), representing respectively the number of stations and the number of queries. The stations are numbered from 1 to N. Each of the following N−1 lines contains two distinct integers U and V (1≤U,V≤N), indicating that there is a tunnel between stations U and V. Each of the following Q lines contains four distinct integers A,B,C,D (1≤A,B,C,D≤N), representing a query: the two train lines are defined by pairs (A,B) and (C,D)

.

Output

For each query, your program must print a line containing an integer representing how many stations the two train lines defined by the query would have in common.

Examples

Input

5 1
1 5
2 5
5 3
5 4
1 2 3 4

Output

1

Input

10 4
1 4
4 5
3 4
3 2
7 3
6 7
7 8
10 8
8 9
6 10 2 5
1 9 5 10
9 10 2 1
5 10 2 9

Output

0
4
0
3
#include <bits/stdc++.h>
using namespace std;
vector<int>g[100005];
int pre[26][100005];
int dep[100005];
int n, q, root;
void dfs(int v, int p, int d)
{
    pre[0][v]=p;
    dep[v]=d;
    int aa=g[v].size();
    for(int i=0; i<aa; i++)
    {
        if(g[v][i]!=p)
            dfs(g[v][i], v, d+1);
    }
}
void init()
{
    dfs(root, -1, 0);
    for(int i=0; i<24; i++)
    {
        for(int j=1; j<=n; j++)
        {
            if(pre[i][j]<0)
                pre[i+1][j]=-1;
            else
                pre[i+1][j]=pre[i][pre[i][j]];
        }
    }
}
int lca(int u, int v)
{
    if(dep[u]>dep[v])
        swap(u, v);
    for(int i=0; i<25; i++)
    {
        if(dep[v]-dep[u] >>i&1)
            v=pre[i][v];
    }
    if(u==v)
        return u;
    for(int i=24; i>=0; i--)
    {
        if(pre[i][u]!=pre[i][v])
        {
            u=pre[i][u];
            v=pre[i][v];
        }
    }
    return pre[0][u];
}
int main()
{
    root=1;
    int a, b;
    scanf("%d%d", &n, &q);
    for(int i=1; i<n; i++)
    {
        scanf("%d%d", &a, &b);
        g[a].push_back(b);
        g[b].push_back(a);
    }
    init();
    for(int i=1; i<=q; i++)
    {
        int aa, bb, cc, dd;
        scanf("%d%d%d%d", &aa, &bb, &cc, &dd);
        int ab=lca(aa, bb);
        int cd=lca(cc, dd);
        int ac=lca(ab, cd);
        if(ab==cd)
        {
            int sum1, sum2;
            sum1=dep[lca(aa, cc)]+dep[lca(bb, dd)]-2*dep[ab]+1;
            sum2=dep[lca(aa, dd)]+dep[lca(bb, cc)]-2*dep[ab]+1;
            if(sum2>sum1)
                sum1=sum2;
            printf("%d\n", sum1);
        }
        else if(ac!=ab&&ac!=cd)
        {
            printf("0\n");
        }
        else if(ac==ab)
        {
            int s1, s2, s3, s4;
            s1=-dep[cd]+dep[lca(aa, cc)]+1;
            s2=-dep[cd]+dep[lca(aa, dd)]+1;
            s3=-dep[cd]+dep[lca(bb, cc)]+1;
            s4=-dep[cd]+dep[lca(bb, dd)]+1;
            int sum=0;
            sum=max(sum, s1);
            sum=max(sum, s2);
            sum=max(sum, s3);
            sum=max(sum, s4);
            printf("%d\n", sum);
        }
        else
        {
            int s1, s2, s3, s4;
            s1=-dep[ab]+dep[lca(aa, cc)]+1;
            s2=-dep[ab]+dep[lca(aa, dd)]+1;
            s3=-dep[ab]+dep[lca(bb, cc)]+1;
            s4=-dep[ab]+dep[lca(bb, dd)]+1;
            int sum=0;
            sum=max(sum, s1);
            sum=max(sum, s2);
            sum=max(sum, s3);
            sum=max(sum, s4);
            printf("%d\n", sum);
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

z6q6k6

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值