Description
Y岛风景美丽宜人,气候温和,物产丰富。Y岛上有N个城市,有N-1条城市间的道路连接着它们。每一条道路都连接某两个城市。幸运的是,小可可通过这些道路可以走遍Y岛的所有城市。神奇的是,乘车经过每条道路所需要的费用都是一样的。小可可,小卡卡和小YY经常想聚会,每次聚会,他们都会选择一个城市,使得3个人到达这个城市的总费用最小。 由于他们计划中还会有很多次聚会,每次都选择一个地点是很烦人的事情,所以他们决定把这件事情交给你来完成。他们会提供给你地图以及若干次聚会前他们所处的位置,希望你为他们的每一次聚会选择一个合适的地点。
Input
第一行两个正整数,N和M。分别表示城市个数和聚会次数。
后面有N-1行,每行用两个正整数A和B表示编号为A和编号为B的城市之间有一条路。
城市的编号是从1到N的。再后面有M行,每行用三个正整数表示一次聚会的情况:小可可所在的城市编号,小卡卡所在的城市编号以及小YY所在的城市编号。
Output
一共有M行,每行两个数Pos和Cost,用一个空格隔开。
表示第i次聚会的地点选择在编号为Pos的城市,总共的费用是经过Cost条道路所花费的费用。
Sample Input
1 2
2 3
2 4
4 5
5 6
4 5 6
6 3 1
2 4 4
6 6 6
Sample Output
2 5
4 1
6 0
数据范围:
100%的数据中,N<=500000,M<=500000。
40%的数据中N<=2000,M<=2000。
题解Here!
- $p$的深度比$x$小:
相当于$dis(p,a)-dis(x,a)=dis(p,b)-dis(x,b)=dis(x,c)-dis(p,c)=w$。
一共增加了$w$,当然不是最优的。
- $p$的深度比$x$大:
相当于$dis(x,a)-dis(p,a)=dis(p,b)-dis(x,b)=dis(p,c)-dis(x,c)=w$。
即:对于$a$,距离减少了$w$,但是对于$b,c$来说,距离都增加了$w$。
也是一共增加了$w$,当然也不是最优的。
到此,我们找到了到$a,b,c$三个点距离最少的点:
三个$LCA$中,深度最大的那个。
于是距离之和就好办了:
我们知道:
$$dis(u,v)=deep[u]+deep[v]-2\times deep[LCA(u,v)]$$
而:
$$dis=dis(x,a)+dis(x,b)+dis(x,c)$$
于是:
$$dis=deep[x]+deep[a]-2\times deep[LCA(x,a)]+deep[x]+deep[b]-2\times deep[LCA(x,b)]+deep[x]+deep[c]-2\times deep[LCA(x,c)]$$
把那些$LCA$换掉:
$$dis=3\times deep[x]+deep[a]+deep[b]+deep[c]-2\times(deep[x]+deep[x]+deep[y])$$
展开,合并同类项:
$$dis=deep[a]+deep[b]+deep[c]-deep[x]-2\times deep[y]$$
哇!这不就是:
$$dis=deep[a]+deep[b]+deep[c]-deep[LCA(a,b)]-deep[LCA(b,c)]-deep[LCA(a,c)]$$
好河蟹啊有木有!
于是这题只要再写个树链剖分求$LCA$即可。
现在就怕$NOIP$考这种思维题。。。
附代码:
#include<iostream>
#include<algorithm>
#include<cstdio>
#define MAXN 500010
using namespace std;
int n,m,c=1;
int head[MAXN],deep[MAXN],son[MAXN],size[MAXN],fa[MAXN],top[MAXN];
struct Edge{
int next,to;
}a[MAXN<<1];
inline int read(){
int date=0,w=1;char c=0;
while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
return date*w;
}
inline void add(int x,int y){
a[c].to=y;a[c].next=head[x];head[x]=c++;
a[c].to=x;a[c].next=head[y];head[y]=c++;
}
void dfs1(int rt){
son[rt]=0;size[rt]=1;
for(int i=head[rt];i;i=a[i].next){
int will=a[i].to;
if(!deep[will]){
deep[will]=deep[rt]+1;
fa[will]=rt;
dfs1(will);
size[rt]+=size[will];
if(size[son[rt]]<size[will])son[rt]=will;
}
}
}
void dfs2(int rt,int f){
top[rt]=f;
if(son[rt])dfs2(son[rt],f);
for(int i=head[rt];i;i=a[i].next){
int will=a[i].to;
if(will!=fa[rt]&&will!=son[rt])dfs2(will,will);
}
}
int LCA(int x,int y){
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]])swap(x,y);
x=fa[top[x]];
}
if(deep[x]>deep[y])swap(x,y);
return x;
}
void work(){
int x,y,z,lca1,lca2,lca3,ans,dis;
while(m--){
x=read();y=read();z=read();
lca1=LCA(x,y);lca2=LCA(y,z);lca3=LCA(x,z);
dis=0;
if(lca1==lca2)ans=lca3;
else if(lca1==lca3)ans=lca2;
else ans=lca1;
dis=deep[x]+deep[y]+deep[z]-deep[lca1]-deep[lca2]-deep[lca3];
printf("%d %d\n",ans,dis);
}
}
void init(){
int x,y;
n=read();m=read();
for(int i=1;i<n;i++){
x=read();y=read();
add(x,y);
}
deep[1]=1;
dfs1(1);
dfs2(1,1);
}
int main(){
init();
work();
return 0;
}