树上倍增法求LCA

 
输入
第一行是单个整数T(T <= 10),表示测试数据的数量。
  对于每个测试数据,在第一行中有两个数字n(2 <= n <= 40000)和m
(1 <= m <= 200),结点数量和查询数量。下面的n-1行每个包含三个数
字i,j,k,分隔在一个空格中,这意味着有一条连接结点i和结点j的道路
长度为k(0 <k <= 40000)。结点是标记为1到n。接下来m行每个都有
不同的整数i和j,你要回答结点i和结点j之间的距离。
 
输出
对于每个测试用例,输出m行。每行代表查询的答案。在每个测试用例后
输出一条平淡的线。
 
样例输入

2
3 2
1 2 10
3 1 15
1 2
2 3
2 2
1 2 100
1 2
2 1

 样例输出

10

25

100

100

 

 

 

解析:这是一个LCA模板,求x,y的LCA后计算x,y至LCA(x,y)的距离和,直接写代码(有注释)

#include<bits/stdc++.h>
using namespace std;
struct Edge//结构体求路径 
{
	int to,next,val;
}e[40005];
int head[40005];
int tot=1;
int f[40005][50];
int g[40005][50];
int d[40005]; 
int n,m;
inline int read()//read快读 
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
	{
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
	{
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
inline int Log(int x)//求一个数的log 
{
	int i=1;
	while(1<<i<x)
	{
		i++;
	}
	return i;
}
inline void add(int u,int v,int w)//将一个路径加上 
{
	e[tot].to=v;
	e[tot].val=w;
	e[tot].next=head[u];
	head[u]=tot++;
}
inline void dfs(int idx,int fa_)//dfs预处理父亲以及到父亲的距离 
{
	for(int i=head[idx];i;i=e[i].next)
	{
		if(e[i].to!=fa_)
		{
			f[e[i].to][0]=idx;
			g[e[i].to][0]=e[i].val;
			dfs(e[i].to,idx);
		}
	}
}
int SS_BZ(int x,int y)//树上倍增函数 
{
	int sum=0;
	if(d[x]<d[y]) //如果x的深度小于y,就先交换x,y,然后将x向上找父亲,直到深度y相同 
	{
		swap(x,y);
		int sum4=0;
		sum4+=d[x]-d[y];
		int sum2=sum4;
		int sum3=0;
		while(sum2)
		{
			if(sum2%2)
			{
				sum+=g[x][sum3];
				x=f[x][sum3];
			}
			sum3++;
			sum2/=2;
		}
	}
	else if(d[x]>d[y]) //如果x的深度大于y,就先直接将x向上找父亲,直到深度y相同 
	{
		int sum4=0;
		sum4+=d[x]-d[y];
		int sum2=sum4;
		int sum3=0;
		while(sum2)
		{
			if(sum2%2)
			{
				sum+=g[x][sum3];
				x=f[x][sum3];
			}
			sum3++;
			sum2/=2;
		}	
	}
	while(1)
	{
		int i=0;
		while(f[x][i]!=f[y][i])//如果x的2^i辈祖先= y的2^i辈祖先,则x,y 的LCA的深度>x,y的2^i辈祖先 
		{
			i++;
		}
		i--;
		if(i<=0)//如果i的值<=0,则结束 
		{
			sum+=(g[x][0]+g[y][0]);
			break;
		}
		sum+=(g[x][i]+g[y][i]);
		x=f[x][i],y=f[y][i];
	}
	return sum;//将x-->LCA(x,y)+LCA(x,y)-->y的值返回 
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)//T组数据 
	{
		//初始化 
		tot=1;
		memset(d,0,sizeof(d));
		memset(f,0,sizeof(f));
		memset(e,0,sizeof(e));
		memset(head,0,sizeof(head));
		memset(g,0,sizeof(g));
		n=read(),m=read();
		int LOG=Log(n);
		//读入边 
		for(int i=1;i<=n-1;i++)
		{
			int x,y,z;
			x=read(),y=read(),z=read();
			add(x,y,z);
			add(y,x,z);
		}
		//预处理 
		dfs(1,0);
		for(int i=1;i<=n;i++) f[1][i]=0;
		for(int i=2;i<=n;i++)
		{
			for(int j=1;j<=LOG;j++)
			{
				f[i][j]=f[f[i][j-1]][j-1];
			}
		}
		for(int i=1;i<=n;i++) g[1][i]=0;
		for(int i=2;i<=n;i++)
		{
			for(int j=1;j<=LOG;j++)
			{
				g[i][j]=g[f[i][j-1]][j-1]+g[i][j-1];
			}
		}
		for(int i=1;i<=n;i++) d[i]=d[f[i][0]]+1;
		//找x,y的距离并输出 
		for(int i=1;i<=m;i++)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			int p=SS_BZ(x,y);
			printf("%d",p);
		}
	}
	
	return 0;
}
/*
2
3 2
1 2 10
3 1 15
1 2
2 3
2 2
1 2 100
1 2
2 1
*/

  

posted @ 2019-07-19 14:28  CZD648  阅读(290)  评论(5编辑  收藏  举报
Live2D