CS Academy E.Triplet Min Sum(最近公共祖先 倍增法模板)
2017-05-26 21:09
507 查看
Triplet Min Sum
Time limit: 2500 msMemory limit: 128 MB
You are given a tree with NN nodes.
Answer QQ queries
of the type:
Given three distinct nodes AA, BB,
and CC,
find the node DD such
that the sum of distances from DD to AA, BB and CC is
minimum.
Standard input
The first line contains two integers NN and QQ.Each of the following N-1N−1 lines
contain two integers, representing two nodes that share an edge.
Each of the following QQ lines
contains three integers AA, BB and CC.
Standard output
For each query print two numbers on a distinct line: the node DD andthe sum of distances from DD to AA, BB and CC.
Constraints and notes
3 \leqN \leq 10^53≤N≤105
1 \leq
Q \leq 10^51≤Q≤105
Node DD can
be equal to one of AA, BB or CC
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 |
解题思路:这是一道比较标准的最近公共祖先的模板题,所以就根据这道题总结一下这类算法,做这道题采用的方法是倍增法,思路如下
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;
}
}
相关文章推荐
- 求LCA最近公共祖先的在线倍增算法模板_C++
- 【讲解+模板】最近公共祖先(LCA)(倍增)
- 洛谷3379 最近公共祖先模板(倍增)
- lca(最近公共祖先)倍增模板【pascal】
- 洛谷 P3379 【模板】最近公共祖先(LCA) (在线倍增+离线Tarjan)
- 【LCA倍增模板】【poj1330】最近公共祖先
- 洛谷3379 最近公共祖先模板(倍增)
- 洛谷3379 最近公共祖先模板(倍增)
- (倍增)最近公共祖先(LCA)模板
- 算法模板之最近公共祖先问题(LCA)
- 最近公共祖先(LCA)之树上倍增法
- 树剖——【模板】最近公共祖先(LCA)
- [置顶] 倍增LCA(最近公共祖先)算法详解
- 4000 【模板】最近公共祖先(LCA)
- 最近公共祖先LCA【模板】
- [笔记]LCA最近公共祖先---倍增在线算法
- Codeforces-425 (Div. 2)-D(最近公共祖先->倍增)
- 洛谷P3379 【模板】最近公共祖先(LCA)(树链剖分)
- 【模板】最近公共祖先(LCA)
- 【洛谷】3379 【模板】最近公共祖先(LCA)