题目链接:C-MMSet2
题意:多次询问,每个点到给定点集的最大值的最小值。
设点集为
s
s
s,
f
(
u
)
=
m
a
x
v
∈
s
(
d
i
s
(
u
,
v
)
)
f(u)=max_{v\in s}(dis(u,v))
f(u)=maxv∈s(dis(u,v)),求
m
i
n
1
<
=
u
<
=
n
f
(
u
)
min_{1<=u<=n}f(u)
min1<=u<=nf(u)
考虑点集只有1个点,那该点到该点的距离为0,就是要求的答案。
考虑点集只有两个点x,y,那这两个点的路径上的点到这两点的最大值是不是一定大于等于
⌈
d
i
s
(
x
,
y
)
/
2
⌉
\lceil dis(x,y)/2 \rceil
⌈dis(x,y)/2⌉。
扩展到多个点,符合答案要求的点的一端一定是点集其中的点,另一端一定是点集任两点路径上的点吧。
结论:点集的直径/2的上取整即为答案。
随便选一个点作为根,预处理倍增数组,以及各点到根节点的距离。
注意到所有的询问点集加起来为
1
0
6
10^6
106,说明
n
∗
l
o
g
2
(
n
)
n*log_2(n)
n∗log2(n)的复杂度可行,对于边长度相同的树的直径,通常可用两次dfs求得,也即第一次从根节点出发找一个最远点,再从最远点出发找一个最远点。各节点与根节点的距离已经预处理出来了,那么遍历点集即可知道最远点x,然后再遍历一次点集,求
m
a
x
(
d
i
s
[
x
]
+
d
i
s
[
y
]
−
2
∗
d
i
s
[
l
c
a
]
)
max(dis[x]+dis[y]-2*dis[lca])
max(dis[x]+dis[y]−2∗dis[lca])
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e5+7;
bool vis[maxn];
struct Edge{
int v,next;
}edge[maxn<<1];
int head[maxn],top;
void add(int u,int v){
edge[top].v=v;
edge[top].next=head[u];
head[u]=top++;
}
vector<int> vv;
int dis[maxn];
int dep[maxn];
int fa[maxn][20];
const int ci=19;
void dfs(int u,int faa,int val){
int v;
dis[u]=val;
for(int i=head[u];i!=-1;i=edge[i].next){
v=edge[i].v;
if(v==faa) continue;
dep[v]=dep[u]+1;
fa[v][0]=u;
for(int j=1;j<=ci;++j) fa[v][j]=fa[fa[v][j-1]][j-1];
dfs(v,u,val+1);
}
}
int lca(int x,int y){
if(dep[x]<dep[y]) swap(x,y);
for(int i=ci;i>=0;--i)
if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
if(x==y) return x;
for(int i=ci;i>=0;--i)
if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int main(){
int n,u,v,siz,x;
memset(head,-1,sizeof(head));
top=0;
int q;
scanf("%d",&n);
for(int i=1;i<n;++i){
scanf("%d%d",&u,&v);
add(u,v),add(v,u);
}
dep[1]=1;
dfs(1,0,0);
scanf("%d",&q);
while(q--){
int maxx=0,mm;
scanf("%d",&siz);
for(int i=1;i<=siz;++i){
scanf("%d",&x);
vv.push_back(x);
if(dis[x]>maxx){
maxx=dis[x];
mm=x;
}
}
if(siz==1){
printf("0\n");
}
else{
maxx=0;
u=mm;
for(int i=0;i<vv.size();++i){
v=vv[i];
int z=lca(u,v);
maxx=max(maxx,dis[u]+dis[v]-2*dis[z]);
}
printf("%d\n",maxx/2+(maxx&1?1:0));
}
vv.clear();
}
return 0;
}
本文详细解析了在树形结构中求解点集直径的算法,通过预处理倍增数组和点到根节点距离,利用两次深度优先搜索(DFS)找到树的直径,并通过点集的特性得出直径/2上取整的最优解。适用于多次询问,每个点到给定点集的最大值的最小值问题。
846

被折叠的 条评论
为什么被折叠?



