**初始化函数**
void init()
{
memset(head,-1,sizeof(head));
memset(dis,0,sizeof(dis));
edgecount=-1;
for(int i=1;i<=n;++i) vis[i]=yes[i]=false,father[i][0]=i;
}
**链式前向星函数**
struct EDGE
{
int from,to,next;
long long w;
/*
Edge(){}是个用来给变量初始化0的函数
fzhead:fzbody{}为结构体赋初值
*/
EDGE(){}//后面别加分号
fzhead:fzbody{}//后面别加分号
}edge[maxn];
/*
i:边的编号;从from到to;w:权值,此处已删除;next:下一条边的编号
head[i]:由i出发的第一条边的编号
仅插入一条从u1开始的边,则next是-1,head[u1]为该边的编号
再读入u1开始的边,则其为第一条边,next是上一条边的编号
*/
void addEdge(int from,int to,int w)//链序和读入顺序相反
{
edge[++edgecount]=EDGE(from,to,w,head[from]);
head[from]=edgecount;
}
**深度搜索函数**
void DFS(int root, int d)//处理每个结点的深度 and 寻找每个点的父结点
{//d是初始根结点的上一层深度,因为有deep[root]=++d;赋值前根结点深度也加了1,初始深度一般算作1
vis[root]=true;
deep[root]=++d;//深度递增
for(int i=head[root];i!=-1;i=edge[i].next)//递归出口就是到达叶子结点
{//链式前向星遍历,遍历的是root的子结点们,子结点的子结点在递归中遍历
if(vis[edge[i].to]==false)
{//无向图 1 到 2 一条边,还有 2 到 1 一条边,不能重复访问,有向图不会出现
father[edge[i].to][0]=root;//子孙节点往上2的0次方层(1层)的祖先为父结点
dis[edge[i].to]=dis[root]+edge[i].w;
DFS(edge[i].to,d);
}//上文的++d先自加后赋值可以使得到此的d就是这层深度,也是对下一层而言的上一层深度
}
}
**LCA初始化函数**
void LCAinit()
{
for(int j=1;j<=18;++j)
{
for(int i=1;i<=n;++i)
{//思想:它的爷爷是它爹的爹
father[i][j]=father[father[i][j-1]][j-1];
/*
i 往上 2 的 j 次方层的祖先
就是 i 往上 2 的 j-1 次方层
再往上 2 的 j-1 次方层的祖先
显然 2 的 j 次方 等于 2 的 j-1 次方 *2(两次 j-1 次方)
每一个点往上 2 的 0 次方层的祖先已经在DFS函数内实现
现在从 1 次方开始,每个结点的 1 次方先实现,再实现每个结点 2 次方
以此类推,所以次方数的 j 在外层,内层是所有结点
每一个次方数都得在所有结点实现后,再进行下一个次方级别的赋值
*/
}
}
}
**LCA查询函数**
int LCA(int x,int y)//传入询问的两个结点
{
if(deep[x]<deep[y]) swap(x,y);//保证询问时 x 的深度比 y 的深度大
for(int i=18;i>=0;--i)//循环的目的是让 x 和 y 处于同深度
{//18只是一个初始化的习惯性数值,2的18次方层的数结点已经够多了
if(deep[father[x][i]]>=deep[y]) x=father[x][i];
/*
从深层开始找
如果 x 往上 2 的 i 次方层的祖先的深度,大于等于 y 的深度的话
x 就移动到它的 2 的 i 次方深度的祖先
从满足深度的大幅度开始调整
2 的 n 次方……16 8 4 2 1选择移动或不移动,可以实现 2 的 n 次方-1所有层数的移动
*/
}
//现在 x 和 y 处于同一层(同一深度)啦
if(x==y) return x;//令 x 与 y 同深度时,如果 x 和 y 相等,那么这就是LCA的答案,否则继续
for(int i=18;i>=0;--i)// x 和 y 一起往上移动
{
if(father[x][i]!=father[y][i])
{//此次向上 2 的 i 次方层,找到的祖先不一致,x 和 y 都移动到找到的祖先位置(赋值给 x y)
x=father[x][i];
y=father[y][i];
}
/*
如果发现往上移动 n 层祖先一致了,那就把 i 自减接着遍历,用更少的层数往上移动
直到最后i==0的时候,只移动一层,father就相等,这个时候循环彻底结束,输出x和y的父结点就行
此时的 x 和 y 已经都被移动到LCA答案结点的子结点位置
*/
}
return father[x][0] ;
}
Description
There are n houses in the village and some bidirectional roads connecting them. Every day peole always like to ask like this “How far is it if I want to go from house A to house B”? Usually it hard to answer. But luckily int this village the answer is always unique, since the roads are built in the way that there is a unique simple path(“simple” means you can’t visit a place twice) between every two houses. Yout task is to answer all these curious people.
Input
First line is a single integer T(T<=10), indicating the number of test cases.
For each test case,in the first line there are two numbers n(2<=n<=40000) and m (1<=m<=200),the number of houses and the number of queries. The following n-1 lines each consisting three numbers i,j,k, separated bu a single space, meaning that there is a road connecting house i and house j,with length k(0Next m lines each has distinct integers i and j, you areato answer the distance between house i and house j.
Output
For each test case,output m lines. Each line represents the answer of the query. Output a bland line after each test case.
Sample Input
2
3 2
1 2 10
3 1 15
1 2
2 3
2 2
1 2 100
1 2
2 1
Sample Output
10
25
100
100
#include
#include
#include
#include
#include
#include
#define fzhead EDGE(int _from,int _to,int _w,int _next)
#define fzbody from(_from),to(_to),w(_w),next(_next)
using namespace std;
const int maxn=1e5+10;
int T,n,m,x,y;
int head[maxn],edgecount,father[maxn][20],deep[maxn];
//father数组表示每个点往上2的次方层的祖先
long long ans,dis[maxn];//dis是从根结点到i结点的权值总路程
bool vis[maxn],yes[maxn];//vis标记结点是否已被访问,yes用于寻找根结点
void init()
{
memset(head,-1,sizeof(head));
memset(dis,0,sizeof(dis));
edgecount=-1;
for(int i=1;i<=n;++i) vis[i]=yes[i]=false,father[i][0]=i;
}
struct EDGE
{
int from,to,next;
long long w;
/*
Edge(){}是个用来给变量初始化0的函数
fzhead:fzbody{}为结构体赋初值
*/
EDGE(){}//后面别加分号
fzhead:fzbody{}//后面别加分号
}edge[maxn];
/*
i:边的编号;从from到to;w:权值,此处已删除;next:下一条边的编号
head[i]:由i出发的第一条边的编号
仅插入一条从u1开始的边,则next是-1,head[u1]为该边的编号
再读入u1开始的边,则其为第一条边,next是上一条边的编号
*/
void addEdge(int from,int to,int w)//链序和读入顺序相反
{
edge[++edgecount]=EDGE(from,to,w,head[from]);
head[from]=edgecount;
}
void DFS(int root, int d)//处理每个结点的深度 and 寻找每个点的父结点
{//d是初始根结点的上一层深度,因为有deep[root]=++d;赋值前根结点深度也加了1,初始深度一般算作1
vis[root]=true;
deep[root]=++d;//深度递增
for(int i=head[root];i!=-1;i=edge[i].next)//递归出口就是到达叶子结点
{//链式前向星遍历,遍历的是root的子结点们,子结点的子结点在递归中遍历
if(vis[edge[i].to]==false)
{//无向图 1 到 2 一条边,还有 2 到 1 一条边,不能重复访问,有向图不会出现
father[edge[i].to][0]=root;//子孙节点往上2的0次方层(1层)的祖先为父结点
dis[edge[i].to]=dis[root]+edge[i].w;
DFS(edge[i].to,d);
}//上文的++d先自加后赋值可以使得到此的d就是这层深度,也是对下一层而言的上一层深度
}
}
void LCAinit()
{
for(int j=1;j<=18;++j)
{
for(int i=1;i<=n;++i)
{//思想:它的爷爷是它爹的爹
father[i][j]=father[father[i][j-1]][j-1];
/*
i 往上 2 的 j 次方层的祖先
就是 i 往上 2 的 j-1 次方层
再往上 2 的 j-1 次方层的祖先
显然 2 的 j 次方 等于 2 的 j-1 次方 *2(两次 j-1 次方)
每一个点往上 2 的 0 次方层的祖先已经在DFS函数内实现
现在从 1 次方开始,每个结点的 1 次方先实现,再实现每个结点 2 次方
以此类推,所以次方数的 j 在外层,内层是所有结点
每一个次方数都得在所有结点实现后,再进行下一个次方级别的赋值
*/
}
}
}
int LCA(int x,int y)//传入询问的两个结点
{
if(deep[x]<deep[y]) swap(x,y);//保证询问时 x 的深度比 y 的深度大
for(int i=18;i>=0;--i)//循环的目的是让 x 和 y 处于同深度
{//18只是一个初始化的习惯性数值,2的18次方层的数结点已经够多了
if(deep[father[x][i]]>=deep[y]) x=father[x][i];
/*
从深层开始找
如果 x 往上 2 的 i 次方层的祖先的深度,大于等于 y 的深度的话
x 就移动到它的 2 的 i 次方深度的祖先
从满足深度的大幅度开始调整
2 的 n 次方……16 8 4 2 1选择移动或不移动,可以实现 2 的 n 次方-1所有层数的移动
*/
}
//现在 x 和 y 处于同一层(同一深度)啦
if(x==y) return x;//令 x 与 y 同深度时,如果 x 和 y 相等,那么这就是LCA的答案,否则继续
for(int i=18;i>=0;--i)// x 和 y 一起往上移动
{
if(father[x][i]!=father[y][i])
{//此次向上 2 的 i 次方层,找到的祖先不一致,x 和 y 都移动到找到的祖先位置(赋值给 x y)
x=father[x][i];
y=father[y][i];
}
/*
如果发现往上移动 n 层祖先一致了,那就把 i 自减接着遍历,用更少的层数往上移动
直到最后i==0的时候,只移动一层,father就相等,这个时候循环彻底结束,输出x和y的父结点就行
此时的 x 和 y 已经都被移动到LCA答案结点的子结点位置
*/
}
return father[x][0] ;
}
int main()
{
scanf("%d",&T) ;
while(T--)
{
scanf("%d %d",&n,&m);
init();
int from,to;
long long w;
for(int i=1;i<=n-1;++i)
{
scanf("%d %d %lld",&from,&to,&w);
addEdge(from,to,w);
addEdge(to,from,w);
yes[to]=true;
}//初始化edgecount为-1,所以从0号边开始存入,但链式前向星遍历不受影响
DFS(1,0);//根结点深度从1开始,先给所有结点找好自己的父结点,然后进行下文循环
LCAinit();
for(int i=1;i<=m;++i)
{
scanf("%d %d",&x,&y);//输入需查询的两个结点
ans=dis[x]+dis[y]-dis[LCA(x,y)]*2;
printf("%lld\n",ans);
}
}
return 0;
}
**初始化函数**
void init()
{
memset(head,-1,sizeof(head));
memset(ans,0,sizeof(ans));
edgecount=-1;
for(int i=1;i<=n;++i) vis[i]=yes[i]=false,father[i]=i,qst[i].clear();
}
**并查集函数**
int find(int x) {return x==father[x]?x:father[x]=find(father[x]);}
void baba(int x,int y) {int fx=find(x);int fy=find(y);father[fx]=fy;}
**链式前向星函数**
struct EDGE
{
int from,to,next;
/*
Edge(){}是个用来给变量初始化0的函数
fzhead:fzbody{}为结构体赋初值
*/
EDGE(){}//后面别加分号
fzhead:fzbody{}//后面别加分号
}edge[maxn];
/*
i:边的编号;从from到to;w:权值,此处已删除;next:下一条边的编号
head[i]:由i出发的第一条边的编号
仅插入一条从u1开始的边,则next是-1,head[u1]为该边的编号
再读入u1开始的边,则其为第一条边,next是上一条边的编号
*/
void addEdge(int from,int to)//链序和读入顺序相反
{
edge[++edgecount]=EDGE(from,to,head[from]);
head[from]=edgecount;
}
**Tarjan实现LCA查询函数**
void tarjan(int root)
{
vis[root]=true;//遍历到的根结点标记已被访问
for(int i=head[root];i!=-1;i=edge[i].next)//递归出口就是到达叶子结点
{//链式前向星遍历,遍历的是root的子结点们,子结点的子结点在递归中遍历
if(vis[edge[i].to]==false)
{//无向图 1 到 2 一条边,还有 2 到 1 一条边,不能重复访问,有向图不会出现
tarjan(edge[i].to);//递归中第二个循环也会经历,把当前结点看做根结点
baba(edge[i].to,root);//合并到祖宗
}
/*
合并前提是所有子结点都已返回
不然退出if还会继续进行i=edge[i].next遍历
不足以彻底让子结点们的父结点if内的tarjan结束
*/
}
for(int i=0;i<qst[root].size();++i)
{//遍历询问,与root相关的有qst[root].size()个询问
if(vis[qst[root][i].first]==true)//注意vector第一维确定,第二维可变长
{//qst[root][i]是与root相关的第i个询问,first是相关结点,second是询问序号
ans[qst[root][i].second]=find(qst[root][i].first);
}//若相关结点已经被遍历到,就用相关结点的祖先,作为这个second序号LCA询问的answer
}
}
Description
A rooted tree is a well-known data structure in computer science and engineering. An example is shown below:
In the figure, each node is labeled with an integer from {1, 2,…,16}. Node 8 is the root of the tree. Node x is an ancestor of node y if node x is in the path between the root and node y. For example, node 4 is an ancestor of node 16. Node 10 is also an ancestor of node 16. As a matter of fact, nodes 8, 4, 10, and 16 are the ancestors of node 16. Remember that a node is an ancestor of itself. Nodes 8, 4, 6, and 7 are the ancestors of node 7. A node x is called a common ancestor of two different nodes y and z if node x is an ancestor of node y and an ancestor of node z. Thus, nodes 8 and 4 are the common ancestors of nodes 16 and 7. A node x is called the nearest common ancestor of nodes y and z if x is a common ancestor of y and z and nearest to y and z among their common ancestors. Hence, the nearest common ancestor of nodes 16 and 7 is node 4. Node 4 is nearer to nodes 16 and 7 than node 8 is.
For other examples, the nearest common ancestor of nodes 2 and 3 is node 10, the nearest common ancestor of nodes 6 and 13 is node 8, and the nearest common ancestor of nodes 4 and 12 is node 4. In the last example, if y is an ancestor of z, then the nearest common ancestor of y and z is y.
Write a program that finds the nearest common ancestor of two distinct nodes in a tree.
Input
The input consists of T test cases. The number of test cases (T) is given in the first line of the input file. Each test case starts with a line containing an integer N , the number of nodes in a tree, 2<=N<=10,000. The nodes are labeled with integers 1, 2,…, N. Each of the next N -1 lines contains a pair of integers that represent an edge --the first integer is the parent node of the second integer. Note that a tree with N nodes has exactly N - 1 edges. The last line of each test case contains two distinct integers whose nearest common ancestor is to be computed.
Output
Print exactly one line for each test case. The line should contain the integer that is the nearest common ancestor.
Sample Input
2
16
1 14
8 5
10 16
5 9
4 6
8 4
4 10
1 13
6 15
10 11
6 7
10 2
16 3
8 1
16 12
16 7
5
2 3
3 4
3 1
1 5
3 5
Sample Output
4
3
#include
#include
#include
#include
#include
#include
#define fzhead EDGE(int _from,int _to,int _next)
#define fzbody from(_from),to(_to),next(_next)
using namespace std;
const int maxn=1e4+10;
int head[maxn],edgecount,father[maxn],ans[maxn];
bool vis[maxn],yes[maxn];//vis标记结点是否已被访问,yes用于寻找根结点
int T,n,m,s,cnt=0;
vector<pair<int,int> > qst[maxn];
//下标是询问结点,first是相关结点,second是询问序号
void init()
{
memset(head,-1,sizeof(head));
memset(ans,0,sizeof(ans));
edgecount=-1;
for(int i=1;i<=n;++i) vis[i]=yes[i]=false,father[i]=i,qst[i].clear();
}
int find(int x) {return x==father[x]?x:father[x]=find(father[x]);}
void baba(int x,int y) {int fx=find(x);int fy=find(y);father[fx]=fy;}
struct EDGE
{
int from,to,next;
/*
Edge(){}是个用来给变量初始化0的函数
fzhead:fzbody{}为结构体赋初值
*/
EDGE(){}//后面别加分号
fzhead:fzbody{}//后面别加分号
}edge[maxn];
/*
i:边的编号;从from到to;w:权值,此处已删除;next:下一条边的编号
head[i]:由i出发的第一条边的编号
仅插入一条从u1开始的边,则next是-1,head[u1]为该边的编号
再读入u1开始的边,则其为第一条边,next是上一条边的编号
*/
void addEdge(int from,int to)//链序和读入顺序相反
{
edge[++edgecount]=EDGE(from,to,head[from]);
head[from]=edgecount;
}
void tarjan(int root)
{
vis[root]=true;//遍历到的根结点标记已被访问
for(int i=head[root];i!=-1;i=edge[i].next)//递归出口就是到达叶子结点
{//链式前向星遍历,遍历的是root的子结点们,子结点的子结点在递归中遍历
if(vis[edge[i].to]==false)
{//无向图 1 到 2 一条边,还有 2 到 1 一条边,不能重复访问,有向图不会出现
tarjan(edge[i].to);//递归中第二个循环也会经历,把当前结点看做根结点
baba(edge[i].to,root);//合并到祖宗
}
/*
合并前提是所有子结点都已返回
不然退出if还会继续进行i=edge[i].next遍历
不足以彻底让子结点们的父结点if内的tarjan结束
*/
}
for(int i=0;i<qst[root].size();++i)
{//遍历询问,与root相关的有qst[root].size()个询问
if(vis[qst[root][i].first]==true)//注意vector第一维确定,第二维可变长
{//qst[root][i]是与root相关的第i个询问,first是相关结点,second是询问序号
ans[qst[root][i].second]=find(qst[root][i].first);
}//若相关结点已经被遍历到,就用相关结点的祖先,作为这个second序号LCA询问的answer
}
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);//一共n个结点,n-1条边
init();//初始化用到了n,一定要在输入后再初始化鸭!!!
for(int i=1,from,to;i<=n-1;++i)
{
scanf("%d %d",&from,&to);
addEdge(from,to);
yes[to]=true;
}//初始化edgecount为-1,所以从0号边开始存入,但链式前向星遍历不受影响
int root;
for(int i=1;i<=n;++i)
{//所有的to都成了true,最后是false的只有唯一一个from
if(yes[i]==false)
{
root=i;
break;
}
}
m=1;//询问个数,此处仅出入一例
int putfrom,putto;
scanf("%d %d",&putfrom,&putto);
//对于1号询问,putfrom和putto都可能是询问结点,另一个为相关结点
qst[putfrom].push_back(make_pair(putto,1));
qst[putto].push_back(make_pair(putfrom,1));
//vector第二维下标自动从0开始存,所以函数内部第二个遍历i=0开始
tarjan(root);//传入根结点序号
//for(int i=1;i<=m;++i) printf("%d\n",ans[i]);此处仅1例
printf("%d\n",ans[1]);
}
return 0;
}