P4271 [USACO18FEB] New Barns P 题解

注意到一个点到连通块中最远的点,必然是两直径端点之一。

我们需要添加一个点并连边,动态维护直径。经典结论是,两个连通块合并,新的直径端点必然是原来两个连通块的 44 个直径之中。

由于这题只新增一个点而非连通块合并,所以我们只需要比较 33 条路径长度即可。

使用 LCT 动态维护加边和求路径长度即可。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <stack>
#include <string>
#include <vector>
using namespace std;

const int N = 2e5 + 5;

int q;

class Union_Find
{
public:
	int fa[N];
	int l[N], r[N];
	void Init()
	{
		for (int i = 0; i < N; i++) fa[i] = l[i] = r[i] = i;
	}
	int find(int u)
	{
		return (fa[u] == u ? u : fa[u] = find(fa[u]));
	}
	void merge(int u, int v, int nl, int nr)
	{
		fa[find(u)] = find(v);
		l[find(v)] = nl, r[find(v)] = nr;
	}
}uf;

class Link_Cut_Tree
{
public:
	struct Node
	{
		int val, son[2], fa, sz, sum;
		bool rev;
	}tr[N];
	void pushup(int u)
	{
		tr[u].sum = tr[tr[u].son[0]].sum + tr[tr[u].son[1]].sum + tr[u].val;
	}
	int get(int x)
	{
		return (x == tr[tr[x].fa].son[1]);
	}
	bool isroot(int x)
	{
		return (tr[tr[x].fa].son[1] != x) && (tr[tr[x].fa].son[0] != x);
	}
	void rev(int u)
	{
		tr[u].rev ^= 1;
		swap(tr[u].son[0], tr[u].son[1]);
	}
	void pushdown(int u)
	{
		if (tr[u].rev)
		{
			rev(tr[u].son[0]);
			rev(tr[u].son[1]);
			tr[u].rev = 0;
		}
	}
	void rotate(int x)
	{
		int y = tr[x].fa, z = tr[y].fa;
		int chkx = get(x), chky = get(y), p = isroot(y);
		tr[y].son[chkx] = tr[x].son[chkx ^ 1];
		if (tr[x].son[chkx ^ 1]) tr[tr[x].son[chkx ^ 1]].fa = y;
		tr[x].son[chkx ^ 1] = y;
		tr[y].fa = x;
		tr[x].fa = z;
		if (z && !p) tr[z].son[chky] = x;
		pushup(y);
		pushup(x);
	}
	void update(int u)
	{
		stack<int> st;
		st.push(u);
		while (!isroot(u))
		{
			u = tr[u].fa;
			st.push(u);
		}
		while (st.size())
		{
			pushdown(st.top());
			st.pop();
		}
	}
	void splay(int u)
	{
		update(u);
		while (!isroot(u))
		{
			int y = tr[u].fa, z = tr[y].fa;
			if (!isroot(y))
			{
				if (get(y) ^ get(u)) rotate(u);
				else rotate(y);
			}
			rotate(u);
		}
	}
	void access(int u)
	{
		int z = u;
		for (int y = 0; u; y = u, u = tr[u].fa)
		{
			splay(u);
			tr[u].son[1] = y;
			pushup(u);
		}
		splay(z);
	}
	int findroot(int u)
	{
		access(u);
		while (tr[u].son[0])
		{
			pushdown(u);
			u = tr[u].son[0];
		}
		splay(u);
		return u;
	}
	void makeroot(int u)
	{
		access(u);
		rev(u);
	}
	void link(int x, int y)
	{
		makeroot(x);
		if (findroot(y) != x)
		{
			tr[x].fa = y;
		}
	}
	void split(int u, int v)
	{
		makeroot(u);
		access(v);
	}
}lct;

int main()
{
	ios::sync_with_stdio(0), cin.tie(0);
	uf.Init();
	cin >> q;
	int idx = 0;
	while (q--)
	{
		char c;
		cin >> c;
		if (c == 'B')
		{
			idx++;
			int p;
			cin >> p;
			lct.tr[idx].val = 1;
			lct.pushup(idx);
			if (~p)
			{
				int nl = uf.l[uf.find(p)], nr = uf.r[uf.find(p)];
				lct.link(idx, p);
				lct.split(idx, nl);
				int v1 = lct.tr[nl].sum - 1;
				lct.split(idx, nr);
				int v2 = lct.tr[nr].sum - 1;
				lct.split(nl, nr);
				int v3 = lct.tr[nr].sum - 1;
				if (v3 >= v1 && v3 >= v2) uf.merge(idx, p, nl, nr);
				else if (v1 >= v2 && v1 >= v3) uf.merge(idx, p, idx, nl);
				else uf.merge(idx, p, idx, nr);
			}
		}
		else
		{
			int x;
			cin >> x;
			int l = uf.l[uf.find(x)], r = uf.r[uf.find(x)];
			lct.split(x, r);
			int ans = lct.tr[r].sum - 1;
			lct.split(x, l);
			ans = max(ans, lct.tr[l].sum - 1);
			cout << ans << "\n";
		}
	}
	return 0;
}
posted @   HappyBobb  阅读(6)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示