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