Triplet Min Sum
Time limit:
2500 ms
Memory limit: 128 MB
Memory limit: 128 MB
You are given a tree with N nodes. Answer Q queries of the type:
- Given three distinct nodes A, B, and C, find the node D such that the sum of distances from D to A, B and C is minimum.
Standard input
The first line contains two integers N and Q.
Each of the following N−1 lines contain two integers, representing two nodes that share an edge.
Each of the following Q lines contains three integers A, B and C.
Standard output
For each query print two numbers on a distinct line: the node D and the sum of distances from D to A, B and C.
Constraints and notes
- 3≤N≤105
- 1≤Q≤105
- Node D can be equal to one of A, B or C
- It can be proved the answer is unique
Input | Output |
---|---|
10 3 1 7 4 7 6 2 8 3 9 8 5 8 2 4 3 4 10 7 1 7 8 8 3 10 6 10 3 | 7 4 3 4 4 5 |
题目大意:存在n个点和n-1条边,有Q次询问,每次询问abc三个点,要求输出这三点的最短路到达哪个点,长度为多少
解题思路:这是一道比较标准的最近公共祖先的模板题,所以就根据这道题总结一下这类算法,做这道题采用的方法是倍增法,思路如下
1.建图完成后,我们根据从其中任意点开始标记层次,最近公共祖先的原理就是根据要求找到最新公共祖先的两个点所在的层次,层次高的往上跳,直到跳到两个点的层次相同,之后大家一块跳,所以根据这个我们可以需要用dfs遍历图完成层次结构的设计
2.我们设置数组fa[i][j]意味着i的i+2^j位置是谁,j为0就是指的是i这个点的父亲是谁,同时我们要初始化fa[i][0]的值,也就是初始化每个点的父亲是谁,这一步会在dfs的时候完成,然后通过两层循环完成所有位置的对于他2^j位置的点的标记
for(j=1;(1<<j)<=n;j++)
{
for(i=1;i<=n;i++)
{
fa[i][j]=fa[fa[i][j-1]][j-1];
}
}
这段代码的意思指的是i的i+2^j位置应该是i+2^(j-1)位置的点的2^(j--1)决定,这个循环代码是倍增法的主要思想之一
3.之后我们就可以比较两个点的最近公共祖先是谁了
int f=dis[x]-dis[y];
// cout<<f<<endl;
for(i=0;(1<<i)<=f;i++)
{
if((1<<i)&f)
x=fa[x][i];
}
f指的是x和y这两个点之间差的层数,然后根据二进制将层次高的点不断地上移,直到两个点到达同一个层次
if(x!=y)
{
for(i=(int)log2(n);i>=0;i--)
{
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
}
x=fa[x][0];
}
这段代码表示的是,当两个点到达同一个层次之后,要是不相同,我们要做的是一起跳,直到只需要一就相同的位置就停止
以上就是LCA的主要内容
对于这道题要注意的是,我们要分别寻找xy,xz,yz三个部分的分别公共祖先是谁,结果会存在着有两个部分的最近公共祖先相同,但是我们不选择,因为最近公共祖先肯定是至少层次要低于或者等于某个点,这意味着对于三个点来说,他一定不是最短的路径,所以不选择相同的那个
#include<iostream>
#include<cstdio>
#include<stdio.h>
#include<cstring>
#include<cstdio>
#include<climits>
#include<cmath>
#include<vector>
#include <bitset>
#include<algorithm>
#include <queue>
#include<map>
#define inf 9999999;
using namespace std;
int fa[100005][30],dis[100005],n;
vector<int> tu[100005];
void dfs(int x,int father,int d)
{
int i;
fa[x][0]=father;
for(int i=0;i<tu[x].size();i++)
{
if(tu[x][i]==father)
continue;
dis[tu[x][i]]=d+1;
dfs(tu[x][i],x,d+1);
}
}
int LCA(int x,int y)
{
int i;
if(dis[x]<dis[y])
swap(x,y);
int f=dis[x]-dis[y];
// cout<<f<<endl;
for(i=0;(1<<i)<=f;i++)
{
if((1<<i)&f)
x=fa[x][i];
}
if(x!=y)
{
for(i=(int)log2(n);i>=0;i--)
{
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
}
x=fa[x][0];
}
return x;
}
int lens(int x,int y)
{
int i=LCA(x,y);
return dis[x]+dis[y]-2*dis[i];
}
int main()
{
int T,x,y,z,i,j;
cin>>n>>T;
for(i=1;i<=n-1;i++)
{
cin>>x>>y;
tu[x].push_back(y);
tu[y].push_back(x);
}
dis[1]=1;
dfs(1,0,1);
/*cout<<"----------------------"<<endl;
for(i=1;i<=n;i++)
{
cout<<"dis["<<i<<"]:"<<dis[i]<<endl;
cout<<"fa["<<i<<"]:"<<fa[i]<<endl;
}
cout<<"----------------------"<<endl;*/
for(j=1;(1<<j)<=n;j++)
{
for(i=1;i<=n;i++)
{
fa[i][j]=fa[fa[i][j-1]][j-1];
}
}
int h1,h2,h3,res;
while(T--)
{
x=y=z=0;
cin>>x>>y>>z;
//int h1,h2,h3,res;
h1=LCA(x,y);
h2=LCA(x,z);
h3=LCA(y,z);
// cout<<"h1 h2 h3:"<<h1<<" "<<h2<<" "<<h3<<endl;
if(h1==h2)
res=h3;
else if(h1==h3)
res=h2;
else if(h2==h3)
res=h1;
int k=lens(x,res)+lens(y,res)+lens(z,res);
cout<<res<<" "<<k<<endl;
}
}