P4271 [USACO18FEB] New Barns P 题解
注意到一个点到连通块中最远的点,必然是两直径端点之一。
我们需要添加一个点并连边,动态维护直径。经典结论是,两个连通块合并,新的直径端点必然是原来两个连通块的
由于这题只新增一个点而非连通块合并,所以我们只需要比较
使用 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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现