CF1916E Happy Life in University
关于我赛时线段树忘了开四倍空间导致白吃了一发罚时这档逝
约定
与
所有在
分析
首先考虑两个点要是没有祖先关系,那必然是选择两个叶子最优。想到枚举 LCA 算贡献,则在每个点
我们反过来考虑。
求出每个点
然后我们考虑两个点如果有祖先关系,那必然是一个是根,一个是叶子。直接找根到所有叶子的 diff 最大值即可。
最后还剩下把
至于为什么对每个点枚举所有它的极浅同色点不会 T,那是因为每个点至多是一个点的极浅同色点。所以每个点最多被枚举一次。
总复杂度是
代码
#include <iostream>
#include <cassert>
#include <vector>
#define ll long long
using namespace std;
vector<int> vec[300005];
vector<int> sm[300005];
int clr[300005];
int f[300005];
int head[300005], nxt[300005], to[300005], ecnt;
void add(int u, int v) { to[++ecnt] = v, nxt[ecnt] = head[u], head[u] = ecnt; }
int L[300005], R[300005], ncnt;
ll ans = 1;
int lcnt = 0;
int n;
struct Segment_Tree {
int mx[1200005], tg[1200005];
inline void tag(int o, int t) { mx[o] += t, tg[o] += t; }
inline void pushdown(int o) {
if (!tg[o])
return;
tag(o << 1, tg[o]);
tag(o << 1 | 1, tg[o]);
tg[o] = 0;
}
inline void pushup(int o) { mx[o] = max(mx[o << 1], mx[o << 1 | 1]); }
void Clear(int o, int l, int r) {
mx[o] = tg[o] = 0;
if (l == r)
return;
int mid = (l + r) >> 1;
Clear(o << 1, l, mid);
Clear(o << 1 | 1, mid + 1, r);
}
void Add(int o, int l, int r, int L, int R, int v) {
if (L <= l && r <= R) {
tag(o, v);
return;
}
pushdown(o);
int mid = (l + r) >> 1;
if (L <= mid)
Add(o << 1, l, mid, L, R, v);
if (R > mid)
Add(o << 1 | 1, mid + 1, r, L, R, v);
pushup(o);
}
int Query(int o, int l, int r, int L, int R) {
if (L <= l && r <= R)
return mx[o];
pushdown(o);
int mid = (l + r) >> 1;
if (R <= mid)
return Query(o << 1, l, mid, L, R);
else if (L > mid)
return Query(o << 1 | 1, mid + 1, r, L, R);
else
return max(Query(o << 1, l, mid, L, R), Query(o << 1 | 1, mid + 1, r, L, R));
}
} seg;
void dfs(int x) {
if (!vec[clr[x]].empty()) {
int t = vec[clr[x]].size() * 1 - 1;
sm[vec[clr[x]][t]].push_back(x);
}
if (!head[x]) {
L[x] = R[x] = ++ncnt;
seg.Add(1, 1, lcnt, ncnt, ncnt, 1);
return;
}
vec[clr[x]].push_back(x);
for (int i = head[x]; i; i = nxt[i]) {
int v = to[i];
dfs(v);
R[x] = R[v];
L[x] = (i == head[x] ? L[v] : L[x]);
}
vec[clr[x]].pop_back();
seg.Add(1, 1, lcnt, L[x], R[x], 1);
for (auto v : sm[x]) seg.Add(1, 1, lcnt, L[v], R[v], -1);
int mx = 1, smx = 0;
for (int i = head[x]; i; i = nxt[i]) {
int v = to[i];
int t = seg.Query(1, 1, lcnt, L[v], R[v]);
if (t >= mx)
smx = mx, mx = t;
else if (t > smx)
smx = t;
}
ans = max(ans, 1ll * mx * smx);
}
signed main() {
int tc;
cin >> tc;
while (tc--) {
cin >> n;
for (int i = 0; i <= n + 1; i++) {
head[i] = 0;
vec[i].clear();
sm[i].clear();
L[i] = R[i] = 0;
}
ans = 1;
lcnt = ncnt = ecnt = 0;
for (int i = 2; i <= n; i++) {
cin >> f[i];
add(f[i], i);
}
for (int i = 1; i <= n; i++) lcnt += (head[i] == 0);
for (int i = 1; i <= n; i++) cin >> clr[i];
dfs(1);
cout << ans << "\n";
seg.Clear(1, 1, lcnt);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?