题目描述:
不难发现课以转换成:给你一颗树, q q q次询问,每次询问给出一些点集,求点集中最大距离,输出 ( 最 大 距 离 + 1 ) / 2 (最大距离+1)/2 (最大距离+1)/2;
方法一:
根据求树的直径的一种方法知道,将任意节点当成根,那么深度最大的一个点一定用到,接下来只要依次求出这个点到其它给出的点的距离,记录最大的即可。那么两点之间的距离如何求呢,其实就是这两个点的深度相加然后减去他们的2*LCA,这个也比较好理解。求LCA的方法这里就不在说明了,可以百度。
#include <bits/stdc++.h>
using namespace std;
int n,lv[300005],a[1000005],ans,fa[300005],son[300005],size[300005],top[300005];
vector<int>v[300005];
void dfs(int x,int f,int deep)
{
fa[x]=f;
lv[x]=deep;
size[x]=1;
for(int i=0;i<v[x].size();i++)
if(v[x][i]!=f)
{
dfs(v[x][i],x,deep+1);
size[x]+=size[v[x][i]];
if(size[son[x]]<size[v[x][i]])
son[x]=v[x][i];
}
}
void dfs2(int x,int p)
{
top[x]=p;
if(son[x])dfs2(son[x],p);
for(int i=0;i<v[x].size();i++)
if(lv[v[x][i]]>lv[x]&&v[x][i]!=son[x])
dfs2(v[x][i],v[x][i]);
}
int lca(int x,int y)
{
while(top[x]!=top[y])
{
if(lv[top[x]]<lv[top[y]])
swap(x,y);
x=fa[top[x]];
}
return lv[x]>lv[y]?y:x;
}
int dis(int x,int y)
{
return lv[x]+lv[y]-2*lv[lca(x,y)];
}
int main(){
int n;
cin>>n;
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
dfs(1,-1,0);
dfs2(1,1);
int q;
cin>>q;
while(q--)
{
int k,x=-1;
scanf("%d",&k);
for(int i=0;i<k;i++)
{
scanf("%d",&a[i]);
if(x==-1||lv[x]<lv[a[i]])
x=a[i];
}
int ans=-1;
for(int i=0;i<k;i++)
{
int c=dis(x,a[i]);
if(c>ans)
ans=c;
}
printf("%d\n",++ans/2);
}
return 0;
}
方法二:
如果只有一次询问,那么我们就可以考虑DP来做这个题,
d
p
(
i
)
dp(i)
dp(i)表示以
i
i
i为根的子树上的点符合条件(这个点是给出的点)的最大深度。当求到
i
i
i的时候我们可以计算以
i
i
i为
l
c
a
lca
lca的两点之间的最大距离,看能不能更新答案,就是最大的两个
d
p
(
k
)
dp(k)
dp(k)(其中
k
k
k是
i
i
i的儿子)相加减去(
i
i
i的深度)*
2
2
2,要注意的是如果只有一个儿子符合条件并且点
i
i
i也是给出的点,还要求这两个点的距离,就是两个点的深度相减。
那么现在多组询问怎么办呢,可以用到虚树(这里是一个虚树的题目,和这个有些相似,写了下题解介绍了下虚树,有兴趣的同学可以看下)。
#include <bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
const long long inf=1e18;
int lv[maxn],fa[maxn],son[maxn],size[maxn],top[maxn];//lca所需变量
int dfsn[maxn],cnt,st[maxn],ans,vis[maxn];//虚树所需变量
int n,a[1000005];
vector<int>v[maxn];
int dp(int x)
{
if(v[x].size()==0)
return lv[x];
int p1=-1,p2=-1;
for(int i=0;i<v[x].size();i++)
{
int c=dp(v[x][i]);
if(c>p1)
{
p2=p1;
p1=c;
}
else if(c>p2)
p2=c;
}
if(p2!=-1)
ans=max(ans,p1+p2-2*lv[x]);
if(vis[x])ans=max(ans,p1-lv[x]);
v[x].clear();
return p1;
}
void dfs(int x,int f,int deep)
{
dfsn[x]=cnt++;
fa[x]=f;
lv[x]=deep;
size[x]=1;
for(int i=0;i<v[x].size();i++)
if(v[x][i]!=f)
{
int to=v[x][i];
dfs(to,x,deep+1);
size[x]+=size[to];
if(size[son[x]]<size[to])
son[x]=to;
}
}
void dfs2(int x,int p)
{
top[x]=p;
if(son[x])dfs2(son[x],p);
for(int i=0;i<v[x].size();i++)
{
int to=v[x][i];
if(lv[to]>lv[x]&&to!=son[x])
dfs2(to,to);
}
}
int lca(int x,int y)
{
while(top[x]!=top[y])
{
if(lv[top[x]]<lv[top[y]])
swap(x,y);
x=fa[top[x]];
}
return lv[x]>lv[y]?y:x;
}
int cmp(int x,int y){
return dfsn[x]<dfsn[y];
}
int main(){
int n;
cin>>n;
for(int i=1;i<n;i++)
{
int x,y,z;
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
cnt=1;
//以点1为根建树
dfs(1,-1,0);
dfs2(1,1);
for(int i=1;i<=n;i++)
v[i].clear();
int q;
cin>>q;
while(q--)
{
int k,x=-1;
scanf("%d",&k);
for(int i=0;i<k;i++)
{
scanf("%d",&a[i]);
vis[a[i]]=1;
}
sort(a,a+k,cmp);
int index=0;
st[0]=1;
for(int i=0;i<k;i++)
{
if(a[i]==1)continue;//1一开始就已经入栈
int tp=st[index];
int lca_=lca(tp,a[i]);
if(lca_==tp)
{
st[++index]=a[i];
}
else
{
while(dfsn[st[index-1]]>dfsn[lca_])
{
v[st[index-1]].push_back(st[index]);
index--;
}
tp=st[index];
if(dfsn[st[index-1]]<dfsn[lca_])
{
v[lca_].push_back(tp);
st[index]=lca_;
st[++index]=a[i];
}
else if(dfsn[st[index-1]]==dfsn[lca_])
{
v[lca_].push_back(tp);
st[index]=a[i];
}
}
}
while(index)
{
v[st[index-1]].push_back(st[index]);
index--;
}
ans=0;
dp(1);
printf("%d\n",(ans+1)/2);
for(int i=0;i<k;i++)
{
v[a[i]].clear();
vis[a[i]]=0;
}
}
return 0;
}