BZOJ 1787 & 1832 [Ahoi2008]Meet 紧急集合 - 倍增LCA

发现两道重题233

一道很裸很裸的倍增lca题。

一开始还以为边权不同,被震惊到了,想了半天无果,正打算搜题解,然后又看了眼题,发现。。。

其实对于这三个节点,其集合点必然在三个lca中的一个,然后这一个一定是深度最深的那一个。原因是答案中从节点爬到lca这一部分值是不变的,变的是从lca爬到集合点的这一部分dist。若选择深度较浅的,必然意味着有两个节点要从深lca节点爬至浅lca节点,这里是二倍的dist;而若选择深lca节点,只需要一个节点从浅lca节点到深的,代价是dist。综上,选择较深的lca节点。

然后就是一道很裸的倍增lca了。。。

一开始想用tarjan,后来发现还要算代价,这样lca求起来就很混乱,于是不如用好写一些的倍增吧。


#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
 
using namespace std;
 
const int maxn=500005;
 
struct edge
{
    int to,next;
}e[maxn<<1];
 
int n,m;
int head[maxn];
int depth[maxn];
int ff[maxn][21];
 
inline void insert(int a,int b)
{
    static int cnt=0;
    e[++cnt].to=b;e[cnt].next=head[a];head[a]=cnt;
    e[++cnt].to=a;e[cnt].next=head[b];head[b]=cnt;
}
bool cmp(int x,int y)
{
    return depth[x]>depth[y];
}
void dfs(int x,int fa)
{
    depth[x]=depth[fa]+1;
    ff[x][0]=fa;
    for(int i=head[x];i;i=e[i].next)
    {
        if(e[i].to==fa)continue;
        dfs(e[i].to,x);
    }
}
void st_chart()
{
    for(int i=1;i<=20;i++)
        for(int j=1;j<=n;j++)
            ff[j][i]=ff[ff[j][i-1]][i-1];
}
int lca(int x,int y)
{
    if(depth[x]<depth[y])swap(x,y);
    int delta=depth[x]-depth[y];
    int pos=0;
    while(delta)
    {
        if(delta&1)x=ff[x][pos];
        pos++;
        delta>>=1;
    }
    if(x==y)return x;
    for(int i=20;i>=0;i--)
        if(ff[x][i]!=ff[y][i])
            x=ff[x][i],y=ff[y][i];
    return ff[x][0];
}
inline int dist(int x,int y)
{
    return depth[x]+depth[y]-2*depth[lca(x,y)];
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        insert(a,b);
    }
    dfs(1,0);
    st_chart();
    int s[3];
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        s[0]=lca(a,b);
        s[1]=lca(a,c);
        s[2]=lca(b,c);
        sort(s,s+3,cmp);
        printf("%d %d\n",s[0],dist(s[0],a)+dist(s[0],b)+dist(s[0],c));
    }
    return 0;
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值