Description
世界干涸,Zyh认为这个世界的人们离不开水,于是身为神的他要将他掌控的仅仅两个水源地放置在某两个不同的城市。这个世界的城市因为荒芜,他们仅仅保留了必要的道路,也就是说对于任意两个城市有且仅有一条可行的道路。更简单的,城市形成了一棵树。
Zyh要将这两个水源放在两个不同的城市。饥渴的人们会选择一个离他们最近的水源,并向其走去。每个城市的人的速度都是相同的,并且两个相邻(有边直接相连)的城市的距离都是1,每个人花费的时间也都是1。zyh想知道对于他的每种放置方案,最迟到达的时间是多少。
Data Constraint
对于30%的数据 n<=1000 , m<=1000
另有10%的数据 n<=200000 , m<=200000 , 并且形成一条链
另有20%的数据 n<=200000, m<=200000 并且树的形态随机生成
对于100%的数据 n<=200000 , m<=200000
Solution
这道题我打的不是正解,所以我讲一下两种解法。
1、我的。我们考虑到一个询问x,y,所有点到达x,y的最远点最近一定满足:这些点到达x,y路径上后,一定是往靠自己最近的x或y走去。所以我们可以将x,y路径平均分成两部分:靠x近的去x,靠y近的去y。我们想x前一半路径上的点z,假设z有一个不经过路径的到达z的最远点距离z为k,那么到达x的距离为deep[x]-deep[z]+k,由于deep[x]固定,所以我们维护每条边的k-deep[z]。所以设出g[x][0]表示x的父亲到达除x以外的子树的路径的最大的k-deep[father(x)]。那么g[x][j]即x往上跳2^j个点后的最大的k-deep[z]。这个可以用倍增维护。同时还要维护一下x前一半路径后到xy之间的lca的点。他们是要经过lca后再到达y的。推导方法和刚才类似。总时间复杂度为O(
NlogN
).
2、正解。我们发现一个性质:到达一个点x的最远点的路径一定有一部分是在这棵树的直径上的。所以我们假设一个询问(x,y)到达树的直径的距离分别为(a,b设a< b)。那么首先答案有可能为各自到达直径的顶端或底端的距离。其次最大值可能为经过(a,b)之间。所以我们维护直径上的每一个点x不经过直径到达的最远点的距离k。那么即有k+p[x]-p[a]。维护k+p[x]即可。(p[x]即直径的标号,1,2,3……)。
Code
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=4e5+5;
int first[maxn],last[maxn],next[maxn],f[maxn][20],g[maxn][20],g1[maxn][20],v[maxn],bz[maxn];
int n,m,i,t,j,k,l,x,y,z,ln,num,ans,deep[maxn],p[maxn],p1[maxn],p2[maxn],q1[maxn],q2[maxn];
void lian(int x,int y){
last[++num]=y;next[num]=first[x];first[x]=num;
}
void dg(){
int i,j,t,k=0,l=0,z=0,q,x,y;
v[j]=j=1;i=0;bz[1]=1;deep[1]=1;
while (i<j){
x=v[++i];
for (t=first[x];t;t=next[t])
if (last[t]!=f[x][0]) v[++j]=last[t],f[v[j]][0]=x,deep[v[j]]=deep[x]+1;
}
while (j){
x=f[v[j]][0];
q=p[v[j--]]+1;
if (q>p[x]) p2[x]=p1[x],p1[x]=p[x],p[x]=q;
else if (q>p1[x]) p2[x]=p1[x],p1[x]=q;
else if (q>p2[x]) p2[x]=q;
}
for (i=1;i<=n;i++)
q1[i]=p[i];
for (x=n;x>=1;x--)
for (t=first[x];t;t=next[t]){
if (last[t]==f[x][0]) continue;
if (p[last[t]]+1==p[x]) g[last[t]][0]=p1[x]-deep[x],g1[last[t]][0]=p1[x]+deep[x];
else g[last[t]][0]=p[x]-deep[x],g1[last[t]][0]=p[x]+deep[x];
}
}
void dg1(){
int t,x,y;
for (i=1;i<=n;i++){
x=v[i];
if (p[x]<q2[x]) p2[x]=p1[x],p1[x]=p[x],p[x]=q2[x];
else if (p1[x]<q2[x])p2[x]=p1[x],p1[x]=q2[x];
else if (p2[x]<q2[x]) p2[x]=q2[x];
for (t=first[x];t;t=next[t]){
if (last[t]==f[x][0]) continue;
if (p[last[t]]+1==p[x]) q2[last[t]]=p1[x]+1;
else q2[last[t]]=p[x]+1;
}
}
}
int lca(int x,int y){
int i,j,t,k=deep[x],l;swap(x,y);
for (j=ln;j>=0;j--)
if (deep[f[x][j]]>=deep[y]) x=f[x][j];
if (x==y) return x;
for (j=ln;j>=0;j--)
if (f[x][j]!=f[y][j]) x=f[x][j],y=f[y][j];
t=f[x][0];
if (p[t]!=max(q1[x],q1[y])+1) ans=max(ans,p[t]+k-deep[t]);
else if (p1[t]!=min(q1[x],q1[y])+1) ans=max(ans,p1[t]+k-deep[t]);
else ans=max(ans,p2[t]+k-deep[t]);
return f[x][0];
}
void find(int x,int l){
int i,j,t,k=deep[x];
if (l<0) return;
for (j=ln;j>=0;j--)
if ((1<<j)&l) ans=max(ans,g[x][j]+k),x=f[x][j];
}
void find1(int x,int y,int l,int z,int k){
int i,j,t;
for (j=ln;j>=0;j--)
if ((1<<j)&l) y=f[y][j];
for (j=ln;j>=0;j--)
if (deep[f[y][j]]>k) ans=max(ans,g1[y][j]-deep[x]*2+z),y=f[y][j];
}
int main(){
//freopen("fan.in","r",stdin);//freopen("data1.out","w",stdout);
scanf("%d",&n);
for (i=1;i<n;i++)
scanf("%d%d",&x,&y),lian(x,y),lian(y,x);
dg();
p[0]=0;dg1();
/*for (i=1;i<=n;i++)
printf("%d\n",g[i][0]);*/
ln=log(n)/log(2);
for (j=1;j<=ln;j++)
for (i=1;i<=n;i++)
f[i][j]=f[f[i][j-1]][j-1],g[i][j]=max(g[i][j-1],g[f[i][j-1]][j-1]),
g1[i][j]=max(g1[i][j-1],g1[f[i][j-1]][j-1]);
scanf("%d",&m);
for (i=1;i<=m;i++){
scanf("%d%d",&x,&y);if (deep[x]>deep[y]) swap(x,y);
ans=q1[y];
t=lca(x,y);ans=max(ans,q2[t]+deep[x]-deep[t]);
k=deep[x]+deep[y]-deep[t]*2;k/=2;
if (x!=t) ans=max(ans,q1[x]);
find(y,min(k,deep[y]-deep[t]-1));
if (x!=t)find1(t,y,k,deep[x],deep[t]);
else find1(t,y,k,deep[x],deep[t]-1);
find(x,deep[x]-deep[t]-1);
printf("%d\n",ans);
}
}