P4115 Qtree4
提供点分树做法。
先考虑静态问题,发现可以点分治做。具体的,对于每个分治重心,求出每个儿子子树内白点到当前重心的距离最大值。将最大和次大加起来即为两条路径拼接。
考虑动态怎么做?首先建出点分树,每个点维护两个 multiset
,记为
那么每一个
考虑修改的时候,只维护这个点到根的路径的
然而常数比较大,可以考虑使用懒惰删除堆替代 multiset
。
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <vector>
#include <map>
#include <queue>
#include <set>
using namespace std;
const int N = 1e5 + 5, INF = 2e9;
int n, m;
vector<pair<int, int> > G[N];
vector<int> NG[N];
int fa[N], sz[N], dep[N], sum[N];
bool del[N];
bool col[N];
int tot, wc;
class My_Heap
{
public:
priority_queue<int> p, q;
void insert(int x)
{
p.push(x);
}
void erase(int x)
{
q.push(x);
}
void update()
{
if (p.size() < q.size()) exit(-1);
while (q.size() && p.top() == q.top())
{
p.pop(), q.pop();
}
}
int sz()
{
if (p.size() < q.size()) exit(-1);
return p.size() - q.size();
}
int top()
{
update();
return p.top();
}
int sec()
{
int x = top();
update();
if (p.empty()) exit(-1);
p.pop();
int y = top();
insert(x);
return y;
}
int res()
{
return top() + sec();
}
}st[N], st2[N], ans;
class TreeCut
{
private:
int dep[N], fa[N], son[N], top[N], id[N], sz[N], cnt = 0;
void dfs1(int u, int father, int depth)
{
dep[u] = depth;
fa[u] = father;
sz[u] = 1;
for (int i = 0; i < G[u].size(); i++)
{
int nx = G[u][i].first;
if (nx == father) continue;
dfs1(nx, u, depth + 1);
sz[u] += sz[nx];
if (sz[son[u]] < sz[nx]) son[u] = nx;
}
}
void dfs2(int u, int tf)
{
top[u] = tf;
id[u] = ++cnt;
if (!son[u]) return;
dfs2(son[u], tf);
for (int i = 0; i < G[u].size(); i++)
{
int nx = G[u][i].first;
if (nx == fa[u] || nx == son[u]) continue;
dfs2(nx, nx);
}
}
public:
void build()
{
dfs1(1, 1, 1);
dfs2(1, 1);
}
int LCA(int x, int y)
{
while (top[x] ^ top[y])
{
if (dep[top[x]] < dep[top[y]])
{
x ^= y;
y ^= x;
x ^= y;
}
x = fa[top[x]];
}
if (dep[x] < dep[y]) return x;
return y;
}
}tc;
void calc_sz(int u, int f)
{
sz[u] = 0;
if (del[u]) return;
sz[u] = 1;
for (auto& j : G[u])
{
if (j.first != f)
{
calc_sz(j.first, u);
sz[u] += sz[j.first];
}
}
}
void find_wc(int u, int f)
{
if (del[u]) return;
int maxn = tot - sz[u];
for (auto& j : G[u])
{
if (j.first != f)
{
find_wc(j.first, u);
maxn = max(maxn, sz[j.first]);
}
}
if (maxn <= tot / 2) wc = u;
}
int in[N];
vector<int> subtree[N];
void solve(int u, int f)
{
if (del[u]) return;
calc_sz(u, 0);
wc = tot = 0;
tot = sz[u];
find_wc(u, 0);
u = wc;
del[u] = 1;
if (f)
{
NG[f].emplace_back(u);
in[u]++;
fa[u] = f;
//cout << "!!!: " << f << " " << u << "\n";
}
for (auto& j : G[u]) solve(j.first, u);
}
void predfs(int u, int f, int w)
{
if (!f) dep[u] = 0;
else dep[u] = dep[f] + 1;
sum[u] = sum[f] + w;
for (auto& j : G[u])
{
if (j.first != f)
{
predfs(j.first, u, j.second);
}
}
}
inline int get_dist(const int& x, const int& y)
{
return sum[x] + sum[y] - 2 * sum[tc.LCA(x, y)];
}
void dfs(int u)
{
if (col[u] == 0)
{
if (fa[u]) st[u].insert(get_dist(u, fa[u]));
subtree[u].emplace_back(u), st2[u].insert(0);
}
for (auto& j : NG[u])
{
dfs(j);
int maxn = -INF;
for (auto& k : subtree[j])
{
subtree[u].emplace_back(k);
int g = get_dist(u, k);
maxn = max(maxn, g);
if (fa[u]) st[u].insert(get_dist(k, fa[u]));
}
if (maxn == -INF) continue;
st2[u].insert(maxn);
}
}
int rtt = 0;
signed main()
{
ios::sync_with_stdio(0), cin.tie(0);
cin >> n;
for (int i = 1; i < n; i++)
{
int u, v, w;
cin >> u >> v >> w;
G[u].emplace_back(make_pair(v, w));
G[v].emplace_back(make_pair(u, w));
}
tc.build();
solve(1, 0);
predfs(1, 0, 0);
//if (!dbg.check()) return -1;
cin >> m;
int cnt = n;
for (int i = 1; i <= n; i++) if (!fa[i]) rtt = i;
dfs(rtt);
for (int i = 1; i <= n; i++)
{
if (st2[i].sz() >= 2) ans.insert(st2[i].res());
}
while (m--)
{
char opt;
cin >> opt;
if (opt == 'C')
{
int x;
cin >> x;
if (col[x] == 0)
{
if (st2[x].sz() >= 2) ans.erase(st2[x].res());
st2[x].erase(0);
if (st2[x].sz() >= 2) ans.insert(st2[x].res());
for (int i = x; fa[i]; i = fa[i])
{
if (st2[fa[i]].sz() >= 2) ans.erase(st2[fa[i]].res());
int pre = st[i].top();
st2[fa[i]].erase(pre);
st[i].erase(get_dist(x, fa[i]));
if (st[i].sz()) st2[fa[i]].insert(st[i].top());
if (st2[fa[i]].sz() >= 2) ans.insert(st2[fa[i]].res());
}
cnt--;
}
else
{
if (st2[x].sz() >= 2) ans.erase(st2[x].res());
st2[x].insert(0);
if (st2[x].sz() >= 2) ans.insert(st2[x].res());
for (int i = x; fa[i]; i = fa[i])
{
if (st2[fa[i]].sz() >= 2) ans.erase(st2[fa[i]].res());
if (st[i].sz())
{
int pre = st[i].top();
st2[fa[i]].erase(pre);
}
st[i].insert(get_dist(x, fa[i]));
st2[fa[i]].insert(st[i].top());
if (st2[fa[i]].sz() >= 2) ans.insert(st2[fa[i]].res());
}
cnt++;
}
col[x] ^= 1;
}
else
{
if (cnt == 0) cout << "They have disappeared.\n";
else if (ans.sz() == 0) cout << "0" << "\n";
else
{
cout << max({ ans.top(), 0 }) << "\n";
}
}
}
return 0;
}
分类:
题解
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现