Codeforces 832D-Misha, Grisha and Underground(LCA)

题目链接:
http://codeforces.com/contest/832/problem/D

题目大意:
给定一棵树,每次给出一组查询a,b,c,求其中两点到另一点的最大重合路径长度

分析:
这里写图片描述

事实上,在一棵树上,由于不存在环,对任意的三点都存在上图的关系(存在一些情况,三点中某点与X重合),即三点之间互相到达的路径必定均经过某点X,那么要求的便是 max(AX,BX,CX) ,由于在树上,根据LCA可以求得AB,BC,AC,列出方程

AX+CX=ACAX+BX=ABBX+CX=BC

一一解得AX,BX,CX即可,取最大值即是结果

代码:



//给一棵树,求两个节点的LCA
#include<iostream>
#include<map>
#include<cstring>

#define pii pair<int, int>

using namespace std;

const int N = 120005;

int fa[N];
map<pii, int> Q;
map<pii, int> Ans;
bool vis[N];
int root;
int n,q;

struct Edge{
    int u, v, nxt, ans;
}e[N * 2], ans[N*6];
int cnt, cntq;
int h[N], hq[N];

void init(){
    memset(h, -1, sizeof(h));
    memset(hq, -1, sizeof(hq));
    cnt = cntq = 0;
}

void add(int u, int v){
    e[cnt].v = v;
    e[cnt].nxt = h[u];
    h[u] = cnt++;
}

void addq(int u, int v){
    ans[cntq].ans = 0;
    ans[cntq].v = v;
    ans[cntq].nxt = hq[u];
    hq[u] = cntq++;
}

int find(int x){
    return x == fa[x] ? x : fa[x] = find(fa[x]);
}

void tarjan(int u,int pre){
    int v;
    fa[u] = u;
    for(int k = h[u]; ~k; k = e[k].nxt){//后序遍历
        v = e[k].v;
        if (v==pre)
            continue;
        tarjan(v,u);
        fa[v] = u;//并入并查集
    }
    vis[u] = true;//后序遍历
    for(int k = hq[u]; ~k; k = ans[k].nxt){//查找LCA
        v = ans[k].v;
        if(vis[v]){
            Ans[make_pair(u,v)] = Ans[make_pair(v,u)] = find(v);//此处为u,v的LCA
        }
    }
}

int t;
const int maxn = 120000;
int deep[maxn];
int a[maxn],b[maxn],c[maxn];

void dfs(int u,int pos,int pre)
{
    deep[u] = pos;
    for (int i = h[u] ; i != -1 ; i = e[i].nxt)
    {
        int v = e[i].v;
        if (v==pre)
            continue;
        dfs(v,pos+1,u);
    }
}



int main()
{
    memset(h,-1,sizeof(h));
    memset(hq,-1,sizeof(hq));

    scanf("%d%d",&n,&q);
    for (int i = 2 ; i <= n ; i ++)
    {
        scanf("%d",&t);
        add(i,t);
        add(t,i);
    }
    dfs(1,0,1);
    memset(vis, false, sizeof(vis));
    for (int i =1 ; i <= q ; i ++)
    {
        scanf("%d%d%d",&a[i],&b[i],&c[i]);
        addq(a[i],b[i]);
        addq(b[i],a[i]);
        addq(b[i],c[i]);
        addq(c[i],b[i]);
        addq(a[i],c[i]);
        addq(c[i],a[i]);
    }
    tarjan(1,1);
    for (int i = 1; i <= q ; i ++)
    {
        int lab = deep[a[i]]+deep[b[i]]-2*deep[Ans[pii(a[i],b[i])]];
        int lac = deep[a[i]]+deep[c[i]]-2*deep[Ans[pii(a[i],c[i])]];
        int lbc = deep[b[i]]+deep[c[i]]-2*deep[Ans[pii(b[i],c[i])]];
        int p = (lab+lac+lbc)/2;
        int x = p - lab;
        int y = p - lac;
        int z = p - lbc;
        //printf("lab %d lac %d lbc %d\n",lab,lac,lbc);
        //printf("%d %d %d\n",x,y,z);
        printf("%d\n",max(x,max(y,z))+1);

        //printf("ab %d ac %d bc %d \n",Ans[pii(a[i],b[i])],Ans[pii(a[i],c[i])],Ans[pii(c[i],b[i])]);
    }


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值