P8805 [蓝桥杯 2022 国 B] 机房

P8805 [蓝桥杯 2022 国 B] 机房

一、问题简析

本题核心思想是求最近公共祖先,这里采用 \(Tarjan\) 算法求解。

以节点 \(1\) 作为树的根节点。令 \(dp_i=\) 节点 \(i\) 到根节点 \(1\) 的点权和; \(w_i\) 为节点 \(i\) 的权值,即该节点的度; 节点 \(t_i\) 为第 \(i\) 个疑问中,节点 \(u_i\)\(v_i\)\(LCA\),则第 \(i\) 个疑问的答案是

\[ans_i=dp_{u_i}+dp_{v_i}-2*dp_{t_i}+w_{t_i} \]

解释:因为 \(t_i\)\(u_i\)\(v_i\)\(LCA\),所以从 \(u_i\)\(v_i\) 必然经过 \(t_i\)\(dp_{u_i}-dp_{t_i}\)\(u_i\)\(t_i\) (除 \(t_i\))的点权和,而\(dp_{v_i}-dp_{t_i}\)\(v_i\)\(t_i\) (除 \(t_i\))的点权和。二者相加后,再加上 \(t_i\) 的权值,即为所求。

二、Code

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

ll quickin(void)
{
	ll ret = 0;
	bool flag = false;
	char ch = getchar();
	while (ch < '0' || ch > '9')
	{
		if (ch == '-')    flag = true;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9' && ch != EOF)
	{
		ret = ret * 10 + ch - '0';
		ch = getchar();
	}
	if (flag)    ret = -ret;
	return ret;
}

typedef pair<int, int> P;
const int MAX = 1e5 + 5;
int N, M;
vector<int> G[MAX];
vector<P> query[MAX];
bool vis[MAX];
int fa[MAX];
ll d[MAX], dp[MAX], ans[MAX]; // d[i] -- 节点i的度; dp[i] -- 节点i到根节点1的点权和 

void init(void)
{
	for (int i = 1; i <= N; ++i)
		fa[i] = i;
}

int find(int x)
{
	if (fa[x] == x)    return x;
	else    return fa[x] = find(fa[x]);
}

void tarjan(int u)
{
	vis[u] = true;
	
	for (auto v : G[u])
	{
		if (!vis[v])
		{
			tarjan(v);
			fa[v] = u;
		}
	}
	
	for (auto p : query[u])
	{
		int v = p.first, id = p.second;
		if (vis[v])
		{
			int t = find(v);     // u和v的LCA
			ans[id] = dp[u] + dp[v] - 2 * dp[t] + d[t]; 
		}
	}
}

void dfs(int u, int fa)
{
	dp[u] = dp[fa] + d[u];
	for (auto v : G[u])
	{
		if (v != fa)
		{
			dfs(v, u); 
		}
	}
}

int main()
{
	#ifdef LOCAL
	freopen("test.in", "r", stdin);
	#endif
	
	N = quickin(), M = quickin();
	for (int i = 1; i < N; ++i)
	{
		int a, b;
		a = quickin(), b = quickin();
		G[a].push_back(b), G[b].push_back(a);
		++d[a], ++d[b];
	}
	for (int i = 0; i < M; ++i)
	{
		int a, b;
		a = quickin(), b = quickin();
		query[a].push_back(P(b, i));
		query[b].push_back(P(a, i));
	}
	
	init();
	dfs(1, 0);
	tarjan(1);
	
	for (int i = 0; i < M; ++i)
		cout << ans[i] << endl;
	
	return 0;
}

posted @ 2024-05-15 22:39  ltign  阅读(13)  评论(0编辑  收藏  举报