加载中...

lca 求两端的距离 跳步数

给一棵树 看能不能往上跳k步

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <queue>
#include <map>
#include <vector>
#include <stack>
#include <set>
#include <sstream>
#include <fstream> 
#include <cmath>
#include <iomanip>
#include <bitset>
#include <unordered_map>
#include <unordered_set>
#include <random>
//#pragma GCC optimize(3,"Ofast","inline")
#define x first
#define y second
#define ios ios::sync_with_stdio(false),cin.tie(0);
#define endl '\n'
#define pb push_back
#define all(x) x.begin(),x.end()
#define all1(x) x.begin()+1,x.end()
 
using namespace std;
 
typedef unsigned long long uLL;
typedef long long LL;
typedef pair<int,int> PII;
 
const int N = 200010,M = 10010,INF = 0x3f3f3f3f,mod = 1e9 + 7;
const double DNF = 0x7f7f7f7f7f7f7f7f,pi = acos(-1.0),eps = 1e-6;
const long long LNF = 0x3f3f3f3f3f3f3f3f;
 
int n,m,k,t;
int l,r;
int h[N],e[N << 1],ne[N << 1],idx;
int fa[N][18],depth[N];
int dist[N];
void add(int a,int b)
{
	e[idx] = b,ne[idx] = h[a],h[a] = idx ++;
}

void bfs()
{
	queue<int> q;
	for(int i = 1;i <= n;i ++) depth[i] = INF;
	depth[0] = 0,depth[1] = 1;
	q.push(1);

	while(q.size())
	{
		int u = q.front();
		q.pop();

		for(int i = h[u];~ i;i = ne[i])
		{
			int j = e[i];
			if(depth[j] > depth[u] + 1)
			{
				depth[j] = depth[u] + 1;
				fa[j][0] = u;
				q.push(j);

				for(int k = 1;k <= 17;k ++)
					fa[j][k] = fa[fa[j][k - 1]][k - 1];
			}
		}
	}
}

int lca(int a,int b)
{
	if(depth[a] < depth[b]) swap(a,b);

	for(int k = 17;~ k;k --)
		if(depth[fa[a][k]] >= depth[b])
			a = fa[a][k];

	if(a == b) return a;

	for(int k = 17;~ k;k --)
		if(fa[a][k] != fa[b][k])
		{
			a = fa[a][k];
			b = fa[b][k];
		}

	return fa[a][0];
}

void dfs(int u,int fa)
{
	for(int i = h[u];~ i;i = ne[i])
	{
		int j = e[i];
		if(j == fa) continue;
		if(dist[j] > dist[u] + 1) 
		{
			dist[j] = dist[u] + 1;
			dfs(j,u);
		}
	}
}

void find()//根据距离找dfs序 找到左右的儿子最远的端点 
{
	for(int i = 1;i <= n;i ++) dist[i] = INF;
	dist[1] = 0;
	dfs(1,0);
	for(int i = 2;i <= n;i ++) if(dist[i] > dist[l]) l = i;
	for(int i = 1;i <= n;i ++) dist[i] = INF;
	dist[l] = 0;
	dfs(l,0);
	for(int i = 1;i <= n;i ++) if(dist[i] > dist[r]) r = i; 
}

void solve(int u,int v,int anc,int k)
{
	if(depth[u] - depth[anc] >= k)
	{
		for(int i = 17;~ i;i --) if(k >> i & 1) u = fa[u][i];
	}
	else
	{
		k = depth[u] + depth[v] - 2 * depth[anc] - k;
		u = v;
		for(int i = 17;~ i;i --) if(k >> i & 1) u = fa[u][i];

	}
	
	cout << u << endl;
}

int main()
{
	ios;
	
	cin >> n;
	for(int i = 1;i <= n;i ++) h[i] = -1;
	for(int i = 1;i < n;i ++)
	{
		int a, b;
		cin >> a >> b;
		add(a,b), add(b,a);
	}
	
	find();//找直径的两个端点 

	bfs();//lca 
	cin >> m;
	while(m --)
	{
		int u, k, v;
		cin >> u >> k;
		int ancl = lca(u,l),ancr = lca(u,r);//与左端点的祖先 与右端点的祖先 
		cout <<"祖先是"<< ancl<< " " << ancr << endl;
		if(depth[u] + depth[l] - 2 * depth[ancl] > depth[u] + depth[r] - 2 * depth[ancr]) v = l;
		else v = r;//最远的端点是l 
//		cout<<"最远的端点是"<<v<<endl; 
		int anc;
		if(v == l) anc = ancl;//确定了祖先 就不再求一次了 
		else anc = ancr;
		if(depth[u] + depth[v] - 2 * depth[anc] < k) cout << -1 << endl;//如果最远的点到u点的距离很短 那就是这个点 
		else solve(u,v,anc,k);
	}

	return 0;
}

posted @ 2022-09-07 17:14  liang302  阅读(17)  评论(0编辑  收藏  举报