BZOJ 1787 紧急集合(LCA)

转换成抽象模型,就是要求一棵树(N个点,有N-1条边表示这个图是棵树)中某一点满足给定三点a,b,c到某一点的距离和最小。那么我们想到最近公共祖先的定义,推出只有集合点在LCA(a,b)、LCA(a,c)、LCA(b,c)中,才能保证距离和最近。

 

# include <cstdio>
# include <cstring>
# include <cstdlib>
# include <iostream>
# include <vector>
# include <queue>
# include <stack>
# include <map>
# include <set>
# include <cmath>
# include <algorithm>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi 3.1415926535
# define eps 1e-9
# define MOD 100000007
# define INF 1000000000
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(int i=a; i<=n; ++i)
# define FO(i,a,n) for(int i=a; i<n; ++i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# define mp make_pair
# define pb push_back
typedef pair<int,int> PII;
typedef vector<int> VI;
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
int Scan() {
    int res=0, flag=0;
    char ch;
    if((ch=getchar())=='-') flag=1;
    else if(ch>='0'&&ch<='9') res=ch-'0';
    while((ch=getchar())>='0'&&ch<='9')  res=res*10+(ch-'0');
    return flag?-res:res;
}
void Out(int a) {
    if(a<0) {putchar('-'); a=-a;}
    if(a>=10) Out(a/10);
    putchar(a%10+'0');
}
const int N=500005;
//Code begin...

struct Edge{int to, next;}edge[N<<1];
int head[N], tot, fa[N][20], deg[N];
queue<int>que;

void add_edge(int u, int v){edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++;}
void init(){tot=0; mem(head,-1);}
void BFS(int root){
    deg[root]=0; fa[root][0]=root; que.push(root);
    while (!que.empty()) {
        int tmp=que.front(); que.pop();
        FO(i,1,20) fa[tmp][i]=fa[fa[tmp][i-1]][i-1];
        for (int i=head[tmp]; i!=-1; i=edge[i].next) {
            int v=edge[i].to;
            if (v==fa[tmp][0]) continue;
            deg[v]=deg[tmp]+1; fa[v][0]=tmp; que.push(v);
        }
    }
}
int LCA(int u, int v){
    if (deg[u]>deg[v]) swap(u,v);
    int hu=deg[u], hv=deg[v], tu=u, tv=v;
    for (int det=hv-hu, i=0; det; det>>=1, i++) if (det&1) tv=fa[tv][i];
    if (tu==tv) return tu;
    for (int i=19; i>=0; --i) {
        if (fa[tu][i]==fa[tv][i]) continue;
        tu=fa[tu][i]; tv=fa[tv][i];
    }
    return fa[tu][0];
}
int main ()
{
    int n, m, u, v, w, x, y, tmp, p;
    init();
    n=Scan(); m=Scan();
    FO(i,1,n) scanf("%d%d",&u,&v), add_edge(u,v), add_edge(v,u);
    BFS(1);
    while (m--) {
        int ans=INF;
        scanf("%d%d%d",&u,&v,&w);
        x=LCA(u,v); tmp=deg[u]+deg[v]-2*deg[x];
        y=LCA(x,w); tmp+=(deg[x]+deg[w]-2*deg[y]);
        if (ans>tmp) p=x, ans=tmp;
        x=LCA(u,w); tmp=deg[u]+deg[w]-2*deg[x];
        y=LCA(x,v); tmp+=(deg[x]+deg[v]-2*deg[y]);
        if (ans>tmp) p=x, ans=tmp;
        x=LCA(v,w); tmp=deg[v]+deg[w]-2*deg[x];
        y=LCA(x,u); tmp+=(deg[x]+deg[u]-2*deg[y]);
        if (ans>tmp) p=x, ans=tmp;
        printf("%d %d\n",p,ans);
    }
    return 0;
}
View Code

复杂度O(n+m*logn).

转载于:https://www.cnblogs.com/lishiyao/p/6606081.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值