CF1534H Lost Nodes 题解

Description

这是一道交互题

给你一棵包含 nn 个节点的树。你需要猜测出这棵树上的两个特殊节点 a,ba,baa 不一定与 bb 不同)。

首先,你会被告知一个整数 rr,满足节点 rr 是节点 a,ba,b 之间的简单路径上的一点。

接下来你可以进行询问,具体的,每次给定整数 rr,交互器会给你当树的根节点为 rr 时,节点 a,ba,b 的最近公共祖先是哪个节点。

特别的,在给定树的形态之后,你需要先给出在所有 a,b,ra,b,r 的可能的取值中,至少需要多少次询问才能保证得出特殊节点的编号。

1n105;1a,b,rn1≤n≤10^5;1≤a,b,r≤n 且节点 rr 在节点 a,ba,b 之间的简单路径上(包括节点 a,ba,b)。

Solution

由于只要知道第一问的策略是什么,所以第二问和第一问差不多。

首先对于 uu 的子树,且 uru\neq r,如果 uu 子树内所有节点的询问值都在子树内,则一定有一个特殊节点在 uu 的子树内,否则一定不在。

所以可以设计状态:设 fuf_u 表示知道特殊节点是否在 uu 的子树内,如果在 uu 的子树内需要找到确切节点,否则要找到 uu 子树内所有点的询问值的最小次数,gug_u 表示如果特殊节点在 uu 的子树内,则为特殊节点值,否则为 uu 子树内所有点的询问值。

v1,v2,,vtv_1,v_2,\ldots,v_tuu 的儿子,策略大概是从前往后枚举 viv_i,如果按照让 fvif_{v_i} 最小的策略询问的第一个叶子的询问值在 viv_i 子树内,那么特殊节点就在 viv_i 子树内,步数就是 fvif_{v_i}。如果询问值不在 uu 的子树内,则 uu 的子树内都没有特殊节点,步数是 11。否则说明特殊节点不在 viv_i 子树内,但在 uu 的子树内,继续枚举 vi+1v_{i+1} 即可。

所以 fu=maxi=1t(fvi+i1)\displaystyle f_u=\max_{i=1}^{t}{\left(f_{v_i}+i-1\right)},最小化这个东西就让 viv_i 按照 fvif_{v_i} 的值从大到小排序即可。


由于在根 rr 处可能有两个子树都有特殊节点,需要特殊处理一下,并且需要分讨 rr 是否是特殊节点:

  1. rr 是特殊节点,贡献为 maxi=1t(fvi+t1)\displaystyle\max_{i=1}^{t}{\left(f_{v_i}+t-1\right)}
  2. rr 不是特殊节点,贡献为 maxi=1tmaxj=i+1t(fvi+fvj+j2)=maxj=2t(fv1+fvj+j2)\displaystyle\max_{i=1}^{t}{\max_{j=i+1}^{t}{\left(f_{v_i}+f_{v_j}+j-2\right)}}=\max_{j=2}^{t}{\left(f_{v_1}+f_{v_j}+j-2\right)}

要求所有根的答案就换根即可。

时间复杂度:O(nlogn)O(n\log n)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 1e5 + 5;

int n;
int f[kMaxN], g[kMaxN], h[kMaxN], dep[kMaxN];
int buc[kMaxN];
std::vector<int> G[kMaxN];

struct SGT {
int mx[kMaxN * 4], tag[kMaxN * 4];
void pushup(int x) {
mx[x] = std::max(mx[x << 1], mx[x << 1 | 1]);
}
void addtag(int x, int v) {
mx[x] += v, tag[x] += v;
}
void pushdown(int x) {
if (tag[x]) {
addtag(x << 1, tag[x]), addtag(x << 1 | 1, tag[x]);
tag[x] = 0;
}
}
void build(int x, int l, int r) {
mx[x] = -1e9, tag[x] = 0;
if (l == r) return;
int mid = (l + r) >> 1;
build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r);
}
void update(int x, int l, int r, int ql, int qr, int v) {
if (l > qr || r < ql) return;
else if (l >= ql && r <= qr) return addtag(x, v);
pushdown(x);
int mid = (l + r) >> 1;
update(x << 1, l, mid, ql, qr, v), update(x << 1 | 1, mid + 1, r, ql, qr, v);
pushup(x);
}
} sgt;

void ins(int x) {
if (!buc[x]++) sgt.update(1, 0, n, x, x, 1e9 + x);
sgt.update(1, 0, n, 0, x, 1);
}

void del(int x) {
if (!--buc[x]) sgt.update(1, 0, n, x, x, -1e9 - x);
sgt.update(1, 0, n, 0, x, -1);
}

int query() { return std::max(sgt.mx[1] - 1, 1); }

void dfs1(int u, int fa) {
dep[u] = dep[fa] + 1;
bool isleaf = 1;
for (auto v : G[u]) {
if (v == fa) continue;
dfs1(v, u);
}
std::sort(G[u].begin(), G[u].end(), [&] (int i, int j) { return f[i] > f[j]; });
for (auto v : G[u]) {
if (v == fa) continue;
ins(f[v]);
}
f[u] = query();
// std::cerr << u << ' ' << f[u] << '\n';
for (auto v : G[u]) {
if (v == fa) continue;
del(f[v]);
}
}

void dfs2(int u, int fa) {
if (fa) ins(g[u]);
for (auto v : G[u]) {
if (v == fa) continue;
ins(f[v]);
}
for (auto v : G[u]) {
if (v == fa) continue;
del(f[v]), g[v] = query(), ins(f[v]);
}
for (auto v : G[u]) {
if (v == fa) continue;
del(f[v]);
}
if (fa) del(g[u]);
for (auto v : G[u]) {
if (v == fa) continue;
dfs2(v, u);
}

std::vector<int> vec;
for (auto v : G[u]) {
if (v == fa) continue;
vec.emplace_back(f[v]);
}
if (fa) vec.emplace_back(g[u]);
std::sort(vec.begin(), vec.end(), std::greater<>());
for (int i = 0; i < vec.size(); ++i) {
h[u] = std::max<int>(h[u], vec[i] + vec.size() - 1);
if (i) h[u] = std::max(h[u], vec[i] + vec[0] + i - 1);
}
}

int ask(int x) {
std::cout << "? " << x << std::endl;
int v;
std::cin >> v;
return v;
}

int solve(int u, int fa) {
bool isleaf = 1;
for (auto v : G[u]) {
if (v == fa) continue;
int val = solve(v, u);
if (val != u) return val;
isleaf = 0;
}
if (isleaf) return ask(u);
else return u;
}

void dickdreamer() {
std::cin >> n;
for (int i = 1; i < n; ++i) {
int u, v;
std::cin >> u >> v;
G[u].emplace_back(v), G[v].emplace_back(u);
}
sgt.build(1, 0, n);
dfs1(1, 0), dfs2(1, 0);
std::cout << *std::max_element(h + 1, h + 1 + n) << std::endl;
int r, a = 0, b = 0;
std::cin >> r;
// std::cerr << f[4] << '\n';
dfs1(r, 0);
for (auto v : G[r]) {
int val = solve(v, r);
if (val != r) {
if (!a) a = val;
else b = val;
}
if (a && b) break;
}
if (!a) a = r;
if (!b) b = r;
std::cout << "! " << a << ' ' << b << std::endl;
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

CF1534H Lost Nodes 题解
https://sobaliuziao.github.io/2025/06/20/post/525cd754.html
作者
Egg_laying_master
发布于
2025年6月20日
许可协议