每日一题 7月8日 Alliances 点到树上点集的连通网的最短距离
题目链接:https://ac.nowcoder.com/acm/problem/13950
题目大意:
树上有k个帮派,每个帮派会占据一些城市。他们控制的点除了自己占领的点。还有再加上这些城市两两之间路径上的所有城市。
有时帮派之间会结盟,这样联盟的点集为这些帮派的点集的并集。他们控制的点除了自己占领的点。还有再加上这些城市两两之间路径上的所有城市。
现在有Q个询问。每次询问选择一个点作为首都。和一些帮派。我们只考虑这些被选择的帮派,并且他们结盟。首都离联盟控制的点最近距离为多少?
思路:我们先对所有的帮派求一个LCA。保存在LCA[i]。每次询问的帮派x1, x2, ...., xm。对LCA[x1], LCA[x2], ... , LCA[xm]求一个LCA。
1.如果首都root没有在LCA的子树里。那么最短距离为dis(root, LCA)。
2.如果在LCA的子树里面。
(1):如果root的子树有帮派原先占领的点。那么这个点到LCA一定经过root。距离为0。我们可以对每个帮派的点的dfs序排序二分。那么dfs序比in[root]大并且dfs最小的点最有可能是root的子树
(2):如果root的子孙没有被占领。那么可以把dfs序中他左边和右边离他最近的两个点拿出来和首都求LCA,这两个LCA里面一定有一个是离它最近的。(情况2其实可以合并在3里面处理)
#include <bits/stdc++.h>
using namespace std;
vector<int> G[500005];
int in[500005], out[500005], T=0;
struct LCA{
int d[500005], fa[500005][22], lg[500005];
void init(int n, int root){//预处理
for(int i = 1; i <= n; ++i){
lg[i] = lg[i-1] + (1 << lg[i-1] == i);
}
dfs(root, 0);
}
void dfs(int now, int father){
in[now]=++T;
fa[now][0]=father; d[now]=d[father]+1;
for(int i=1; i<=lg[d[now]]; ++i){
fa[now][i]=fa[fa[now][i-1]][i-1];
}
for(auto x: G[now]){
if(x!=father){
dfs(x, now);
}
}
out[now]=T;
}
int lca(int x, int y){//LCA
if(d[x]<d[y]) swap(x, y);
while(d[x]>d[y]){
x=fa[x][lg[d[x]-d[y]]-1];
}
if(x==y) return x;
for(int k=lg[d[x]]-1; k>=0; --k){
if(fa[x][k]!=fa[y][k]){
x=fa[x][k], y=fa[y][k];
}
}
return fa[x][0];
}
int dis(int x, int y){
return d[x]+d[y]-2*d[lca(x, y)];
}
}lca;
vector<int> b[500005], xw;
vector<pair<int, int> > dset[500005];//每个帮派的dfs序和点
int LCA[500005];
int slove(int root){
int Lca=0;
for(auto x: xw){
if(Lca==0) Lca=LCA[x];
else Lca=lca.lca(Lca, LCA[x]);
}
if(in[root]<in[Lca]||in[root]>out[Lca]){//在LCA的子树外
return lca.dis(Lca, root);
}
int ans=1<<30;
pair<int, int> pos1={0 , 0}, pos2={1<<30, 0};//在LCA的子树内
for(auto x: xw){
int pos=lower_bound(dset[x].begin(), dset[x].end(), make_pair(in[root], 0))-dset[x].begin();
if(pos<=dset[x].size()-1){//求最大的小等于dfs[root]的点
if(dset[x][pos].first<pos2.first){
pos2=dset[x][pos];
}
}
if(pos>0){//求最小的大等于dfs[root]的点
pos--;
if(dset[x][pos].first>pos1.first){
pos1=dset[x][pos];
}
}
}
if(pos1.second){
ans=min(ans, lca.dis(root, lca.lca(root, pos1.second)));
}
if(pos2.second){
ans=min(ans, lca.dis(root, lca.lca(root, pos2.second)));
}
return ans;
}
int main(){
int n, x, y, k; scanf("%d", &n);
for(int i=1; i<n; i++){
scanf("%d%d", &x, &y);
G[x].push_back(y);
G[y].push_back(x);
}
lca.init(n+3, 1);
scanf("%d", &k);
for(int i=1; i<=k; i++){
int m; scanf("%d", &m);
while(m--){
scanf("%d", &x);
b[i].push_back(x);
}
}
for(int i=1; i<=k; i++){
int Lca=0;
for(auto x: b[i]){
dset[i].push_back({in[x], x});
if(Lca==0) Lca=x;
else Lca=lca.lca(Lca, x);
}
LCA[i]=Lca;
sort(dset[i].begin(), dset[i].end());
}
int q; scanf("%d", &q);
while(q--){
int root, m;
scanf("%d%d", &root, &m);
xw.clear();
for(int i=1; i<=m; i++){
scanf("%d", &x);
xw.push_back(x);//每次询问的帮派的集合
}
printf("%d\n", slove(root));
}
return 0;
}
查看19道真题和解析
