旅行
- Description
N-1座桥连接着N个岛屿,每座桥都连接着某两个不同的岛屿,从任意一个岛屿都可以到达所有的其他岛屿,过桥需要缴纳人民币1元的过桥费。
由于某些不可透露的原因,Jason和他的2个小伙伴可以在任意一个岛屿集合,但是希望总过桥费最少。
现在,由你来确定集合的岛屿编号,使得总过桥费最少。
- Input Format
输入数据有input.txt提供。第一行有两个整数N和M,表示岛屿的个数和询问次数,岛屿编号从1到N。
接下来N-1行,每行有两个正整数X、Y,表示编号为X的岛屿和编号为Y的岛屿之间有一座桥。
最后还有M行,每行有三个正整数A、B、C,表示Jason和他的两个小伙伴所在岛屿的编号。
- Output Format
结果输出到output.txt。一共有M行,每行两个整数P、Q,用一个空格隔开。其中第i行表示第i次询问集合的地点在编号为P的岛屿,需要花费的最少过桥费为Q。
- Sample Output
5 5
1 2
1 3
2 4
2 5
1 1 1
1 1 2
1 2 3
4 5 3
5 2 3
- Sample Output
1 0
1 1
1 2
2 4
2 3
- Hint
30%的数据中,N≤200,M≤200;
60%的数据中,N≤2,000,M≤2,000;
100%的数据中,N≤200,000,M≤200,000;
- 分析
暴力解法:枚举每一个结点作为集合点,跑一遍树,记录答案,复杂度为
O(n2)
。
正解:对于一棵树上的三个点,我们可以分成一下三种情况:
- 三个点两两的最近公共祖先相同,即三个点分别属于一个结点的三颗子树。
- 一个结点是另两个点最近公共祖先的祖先。
- 三个结点在一条链上。
这样一来就可以很便捷地处理路径长度了。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define Max 200001
using namespace std;
struct Edge{int to,next;}E[2*Max];
int Deep[Max],Father[Max][22],last[Max],n,m,tot,x,y,z,ans1,ans2,ans3,ans;
void Addline(int u,int v){
E[++tot].to=v; E[tot].next=last[u]; last[u]=tot;
E[++tot].to=u; E[tot].next=last[v]; last[v]=tot;
}
void Dfs(int fa,int u){
Father[u][0]=fa; Deep[u]=Deep[fa]+1;
for (int i=1;i<=21;i++) Father[u][i]=Father[Father[u][i-1]][i-1];
for (int i=last[u];i;i=E[i].next){
if (fa==E[i].to) continue;
Dfs(u,E[i].to);
}
}
int Lca(int u,int v){
if (Deep[v]>Deep[u]) swap(u,v);
for (int i;Deep[u]>Deep[v];u=Father[u][i-1]){
for (i=1;i<=21 && Deep[Father[u][i]]>=Deep[v];i++);
}
for (int i;u!=v;u=Father[u][i-1],v=Father[v][i-1]){
for (i=1;i<=21 && Father[u][i]!=Father[v][i];i++);
}
return u;
}
void Work(int x,int y,int z){
int xy=Lca(x,y),yz=Lca(y,z),xz=Lca(x,z);
if (xy!=x && xy!=y) printf("%d %d\n",xy,(Deep[x]+Deep[y]-2*Deep[xy])+(Deep[xy]+Deep[z]-2*Deep[Lca(xy,z)]));
else if (xy==x) printf("%d %d\n",x,(Deep[y]-Deep[x])+(Deep[x]+Deep[z]-2*Deep[xz]));
else printf("%d %d\n",y,(Deep[x]-Deep[y])+(Deep[y]+Deep[z]-2*Deep[yz]));
}
int main(){
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<n;i++){
scanf("%d%d",&x,&y);
Addline(x,y);
}
Dfs(0,1);
for (int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
/*if (Deep[x]<Deep[y]) swap(x,y);
if (Deep[x]<Deep[z]) swap(x,z);
if (Deep[y]<Deep[z]) swap(y,z);*/
int xy=Lca(x,y),yz=Lca(y,z),xz=Lca(x,z);
if (xy==yz && yz==xz){
printf("%d %d\n",xy,Deep[x]+Deep[y]+Deep[z]-3*Deep[xy]);
continue;
}
if (xz==yz) Work(x,y,z);
else if (xy==yz) Work(x,z,y);
else if (xy==xz) Work(y,z,x);
}
fclose(stdin); fclose(stdout);
return 0;
}