[bzoj1787][Ahoi2008]Meet 紧急集合&&[bzoj1832][AHOI2008]聚会

8 篇文章 0 订阅

Description

给出一棵树,每次询问到某三个点x,y,z距离和最小的点是哪个和最小距离。
n,m<=5*10^5,边权为1

Solution

如果只有两个点,那么答案是什么?
很显然是lca(x,y)!
那么三个点呢?
我们可以发现,对于lca(x,y),lca(x,z),lca(y,z)这三个点,必然有两个点是相同的。
那么对于剩下一个点到那个相同的点的路径上,很明显越靠近单独的那个点答案越小。
于是求出lca判断一下就好了。
不过这道题tarjan似乎很麻烦,还是倍增好了。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a) for(int i=last[a];i;i=next[i])
#define N 500005
using namespace std;
int last[N],la[N],next[N*2],t[N*2];
int n,q,x,y,z,l,d[N],f[N][19];
bool bz[N];
void add(int x,int y) {
    t[++l]=y;next[l]=last[x];last[x]=l;
}
void dfs(int x) {
    fo(j,1,18) f[x][j]=f[f[x][j-1]][j-1];
    rep(i,x) if (t[i]!=f[x][0]) {
        f[t[i]][0]=x;
        d[t[i]]=d[x]+1;
        dfs(t[i]);
    }
}
int lca(int x,int y) {
    if (d[x]<d[y]) swap(x,y);
    fd(j,18,0) if (d[f[x][j]]>d[y]) x=f[x][j];
    if (d[x]!=d[y]) x=f[x][0];
    fd(j,18,0) if (f[x][j]!=f[y][j]) x=f[x][j],y=f[y][j];
    if (x!=y) return f[x][0];else return x;
}
int main() {
    scanf("%d%d",&n,&q);
    fo(i,1,n-1) scanf("%d%d",&x,&y),
    add(x,y),add(y,x);
    d[1]=1;dfs(1);
    for(;q;q--) {
        scanf("%d%d%d",&x,&y,&z);int len=d[x]+d[y]+d[z];
        int a=lca(x,y),b=lca(x,z),c=lca(y,z);
        if (a==b) {int u=lca(c,x);printf("%d %d\n",c,len-d[c]-2*d[u]);}
        else if (a==c) {int u=lca(b,y);printf("%d %d\n",b,len-d[b]-2*d[u]);} 
        else {int u=lca(a,z);printf("%d %d\n",a,len-d[a]-2*d[u]);}
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值