Alliances | LCA、dfs序

Alliances

https://ac.nowcoder.com/acm/problem/13950

  1. 题意简化:
    首先给出一棵树,其次询问一个点集,求包含这个点集的最小生成树与询问点x的最短距离
  2. 题目思路
    首先考虑,如何确定这个点集的最小生成树:首先跑一个LCA,找出所有点公共的LCA,那么这个最小生成树的点集根节点(也就可以确定了)
    之后就可以考虑这两种情况:
    1.如果询问点,不在这个子树内:即
    图片说明
    绿色为询问点,由红色点生成的生成树可知,是图中三角形的点,显然绿色点不在红色点两点的祖先子树中,由于树上两点路径唯一,那么绿色点要想通过子树的点,就必须经过这个祖先,所以此时就是询问点到LCA的距离
    2.如果询问点在子树内,可以考虑如果一个点存在于子树中,那么可以考虑为这个点到公共LCA的路径都被覆盖。
    如图所示
    所以说如果当前这个点在子树中且被覆盖的话,总会有一条路径经过他,这条路径就是从它下方(或者它自己)的一个点出发经过该点到达公共祖先LCA 所以此时只需要判断一下,询问点与所有点求了lca之后,有没有可能询问点是作为lca出现的,如果存在一个点使得它成为两点lca那么说明,他在点集的子树中,否则不在子树中。 此时如果只这么写的话会t掉,所以优化一下,加一个二分,刚刚也说到了,使它作为lca的点必然在它的下方,所以dfs序必然会大于等于它。 所以我们只需要二分找一下第一个大于等于它的dfs序的点,看该点是否满足,如果该点满足,那么即成立 至此.解决了两个细问题 最后一个:如何求最短路径呢? 考虑因为它的下方不存在任何标记点,所以说它的顶端一定链接在了点集的最小生成树上,那么如果它存在的话,这条链便也会存在在这个生成树上,所以最短距离就是当前点与 当前点与点集最小生成树上连接点的最短距离

图片说明

红色点代表点集,绿色的即为点集最小生成树,蓝色为询问点
此时最短距离,就是蓝色到最小生成树的距离,这个距离怎么求呢?其实就是他与树中某个点的lca,一定是他上一个点,可以推结论证明: 假设它的链接点没有其他子树,因为lca一定存在点集里,又因为dfs序的遍历顺序,所以这个lca就是 假设它的连接点有其他子树,那么上一个dfs序的点绝对在这里,不同子树间的lca 一定是根 **Code:**

/*** keep hungry and calm CoolGuang!***/
#pragma GCC optimize(2)
//#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pp;
const ll INF=1e17;
const int maxn=5e5+6;
const int mod=998244353;
const double eps=1e-3;
inline bool read(ll &num)
{char in;bool IsN=false;
    in=getchar();if(in==EOF) return false;while(in!='-'&&(in<'0'||in>'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}

ll n,m,p;
int dfn[maxn],deep[maxn],f[maxn][21];///时间戳 深度 fa数组
vector<int>v[maxn],q[maxn],g;
int top[maxn];
int ldfn = 0;
void dfs(int u,int fa){
    deep[u] = deep[fa] + 1;
    f[u][0] = fa;dfn[u] = ++ldfn;
    for(int k=1;k<=20;k++) f[u][k] = f[f[u][k-1]][k-1];
    for(int e:v[u]){
        if(e == fa) continue;
        dfs(e,u);
    }
}
int LCA(int u,int v){///求lca
    if(deep[u] < deep[v]) swap(u,v);
    for(int k=20;k>=0;k--) if(deep[v]<=deep[f[u][k]]) u = f[u][k];
    if(u == v) return u;
    for(int k=20;k>=0;k--){
        if(f[u][k]!=f[v][k]){
            u = f[u][k];
            v = f[v][k];
        }
    }
    return f[u][0];
}
int pos = 0;
int cmp(int a,int b){
    return dfn[a] < dfn[b];
}
ll dis(int u,int v){
    int lca = LCA(u,v);
    return deep[u]+deep[v]-2*deep[lca];
}
int main(){
    read(n);
    for(int i=1;i<=n-1;i++){
        ll x,y;read(x);read(y);
        v[x].push_back(y);
        v[y].push_back(x);
    }
    dfs(1,1);
    read(m);
    for(int i=1;i<=m;i++){
        ll t;read(t);
        for(int k=1;k<=t;k++){
            ll x;read(x);
            q[i].push_back(x);
            if(k==1) top[i] = x;
            else top[i] = LCA(top[i],x);
        }
        sort(q[i].begin(),q[i].end(),cmp);
    }
    read(p);
    for(int i=1;i<=p;i++){
        ll op,t;read(op);read(t);
        g.clear();
        for(int k=1;k<=t;k++){
            ll x;read(x);
            if(k == 1) pos = top[x];
            else pos = LCA(pos,top[x]);
            g.push_back(x);
        }
        int lca = LCA(op,pos);
        ll ans = deep[pos]+deep[op]-2*deep[lca];
        if(lca != pos) printf("%lld\n",ans);
        else{///说明在子树
            ans = 1e9+7;
            for(int e:g){
                int sz = q[e].size();
                int l = 0,r = sz-1;
                int pos = sz;
                while(l<=r){
                    int mid = (l+r)/2;
                    if(dfn[q[e][mid]]>=dfn[op]){
                        r = mid-1;
                        pos = mid;
                    }
                    else l = mid+1;
                }
                if(pos != sz) ans = min(ans,dis(op,LCA(op,q[e][pos])));
                if(pos != 0) ans = min(ans,dis(op,LCA(op,q[e][pos-1])));
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}
/**
7
1 2
1 3
2 4
2 5
3 6
3 7
2
2 6 7
1 4
3
1 1 1
**/


全部评论
被这个点给搞蒙了,有些指代不明,读得难受
点赞 回复 分享
发布于 2020-10-15 17:09

相关推荐

沉淀一会:**圣经 1.同学你面试评价不错,概率很大,请耐心等待;2.你的排名比较靠前,不要担心,耐心等待;3.问题不大,正在审批,不要着急签其他公司,等等我们!4.预计9月中下旬,安心过节;5.下周会有结果,请耐心等待下;6.可能国庆节前后,一有结果我马上通知你;7.预计10月中旬,再坚持一下;8.正在走流程,就这两天了;9.同学,结果我也不知道,你如果查到了也告诉我一声;10.同学你出线不明朗,建议签其他公司保底!11.同学你找了哪些公司,我也在找工作。
点赞 评论 收藏
分享
尊嘟假嘟点击就送:加v细说,问题很大
点赞 评论 收藏
分享
1 收藏 评论
分享
牛客网
牛客企业服务