[CF1062E] Company 题解
[CF1062E] Company 题解
前置芝士:
问题 问题- 利用
转换树上问题
题目描述
给定一颗树,有若干个询问,每个询问给出
多点
见微知著,先考虑多个点的
结论:
假设多个点组成的序列为
其中
翻译成人话就是
证明:
设最终求出的
为
存在性:
即证明
中剩余点都有 作为祖先。 设
的子树中 最大的节点为 ,显然子树中 最小的节点为 本身,此子树在欧拉序上是一段连续的区间。 因此对于
内所有的元素都在 的子树内部, 中剩余点都有 作为祖先。 最优性,根据
的定义,显然所有深度大于 的节点不满足它是 的公共祖先。 证毕。
思路
进一步考虑删除。
根据上一标题下的结论可以发现对答案有影响的节点只有
更多的细节可以看代码
实现
时间瓶颈在 RMQ 与 LCA 上,RMQ 上我用了线段树才不会告诉你ST表调不出来呢Q^Q,多了一个
时间复杂度:
// Problem: Company
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF1062E
// Memory Limit: 250 MB
// Time Limit: 2000 ms
// Author: Moyou
// Copyright (c) 2022 Moyou All rights reserved.
// Date: 2023-01-30 11:32:04
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <iostream>
#include <map>
#include <queue>
#include <stack>
#include <tuple>
#include <unordered_map>
#define x first
#define y second
#define int long long
#define speedup (ios::sync_with_stdio(0), cin.tie(0), cout.tie(0))
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 2e5 + 10;
vector<int> g[N];
int pos[N], idx, dfn[N << 1], dep[N], vis[N << 1];
int st[21][N], lg[N << 1];
int n, m;
void dfs(int p, int fa)
{
dfn[p] = ++idx;
vis[idx] = p;
pos[p] = idx;
for (auto i : g[p])
{
if (i == fa)
continue;
dep[i] = dep[p] + 1;
dfs(i, p);
vis[++idx] = p;
}
}
int Min(int a, int b)
{
return pos[a] < pos[b] ? a : b;
}
void ST()
{
lg[1] = 0;
for (int i = 2; i <= (N << 1); i++)
lg[i] = lg[i >> 1] + 1;
for (int i = 1; i <= (N << 1); i++)
st[0][i] = vis[i];
for (int i = 1; i <= lg[(N << 1) - 1]; i++)
for (int j = 1; j + (1 << i) <= (N << 1); j++)
st[i][j] = Min(st[i - 1][j], st[i - 1][j + (1 << i - 1)]);
}
int LCA(int u, int v)
{
int l = pos[u], r = pos[v];
if (l > r)
swap(l, r);
int k = lg[r - l + 1];
return Min(st[k][l], st[k][r - (1 << k) + 1]);
}
struct owo
{
int l, r, max, min = INF;
};
struct SegmentTree
{
owo t[N << 2];
inline void up(owo &f, owo &ls, owo &rs)
{
f.max = max(ls.max, rs.max);
f.min = min(ls.min, rs.min);
}
inline void up(int k)
{
up(t[k], t[k << 1], t[k << 1 | 1]);
}
void build(int k, int l, int r)
{
t[k].l = l, t[k].r = r;
if (l == r)
t[k] = {l, r, dfn[l], dfn[l]};
else
{
int mid = l + r >> 1;
build(k << 1, l, mid), build(k << 1 | 1, mid + 1, r);
up(k);
}
}
owo query(int k, int ll, int rr)
{
int l = t[k].l, r = t[k].r, mid = l + r >> 1;
if (ll <= l && rr >= r)
return t[k];
if (ll > mid)
return query(k << 1 | 1, ll, rr);
else if (rr <= mid)
return query(k << 1, ll, rr);
auto L = query(k << 1, ll, rr), R = query(k << 1 | 1, ll, rr);
owo tmp = {0, 0, 0, 0};
up(tmp, L, R);
return tmp;
}
} tr;
signed main()
{
speedup;
cin >> n >> m;
for (int i = 2; i <= n; i++)
{
int tmp;
cin >> tmp;
g[tmp].push_back(i);
}
dep[1] = 0;
dfs(1, 0), ST();
// cout << dfn[3] << endl;
tr.build(1, 1, N);
for (int i = 1; i <= m; i++)
{
int l, r;
cin >> l >> r;
auto tmp = tr.query(1, l, r);
int u = tmp.min, v = tmp.max;
u = vis[u], v = vis[v];
// 找到删除 u/v 后的区间最小值,重新求一遍 LCA
int uu = min((l <= u - 1 ? tr.query(1, l, u - 1).min : INF), (r >= u + 1 ? tr.query(1, u + 1, r).min : INF));
int vv = max((l <= v - 1 ? tr.query(1, l, v - 1).max : -INF), (r >= v +1 ? tr.query(1, v + 1, r).max : -INF));
uu = vis[uu], vv = vis[vv];
int d1 = LCA(uu, v), d2 = LCA(vv, u);
// d1 => 删u,d2 => 删v
if (dep[d1] >= dep[d2])
cout << u << ' ' << dep[d1] << "\n";
else
cout << v << ' ' << dep[d2] << "\n";
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!