UOJ618 聚会2 题解
题意: 有一棵
个点的无根树,对于每个 ,求选 个点的最大权值。定义一种方案的权值为,满足“所有选中的点到该点的距离之和最小”的点的个数。 。
首先可以发现,对于一种方案,对于当前点,沿一条边移动后,边对面的子树中选中的点距离
首先,当
为了最大化合法点的数量,一定是将
可以发现,合法的子树的根一定组成树上的一个连通块,且
于是,做法就浮出水面了:对于每个点,处理出它在哪个
思路就是这样,实现上有一些必要的算法:
- 对于删点动态维护直径,可以通过线段树来做。线段树的区间
保存钦定端点编号在 内的直径两端,则每次合并两个儿子时,可以暴力求 遍 dis 合并出父亲区间的答案。这是因为两个点集的并的直径端点一定是从两个点集各自直径的端点中选出的。结合 LCA 即可将复杂度做到 。 - 对于计算在哪个
被删掉,感性理解一下可以发现, 超过 时一定会被扔掉,超过某个 时也会被扔掉,所以取 即可。
From Unique_Hanpi, changed by cxm1024
#include <bits/stdc++.h>
#define lowbit(x) (x & -x)
#define eb emplace_back
#define pb push_back
#define mp make_pair
using namespace std;
typedef long long ll;
const int N = 2e5+5;
const int Mod = 998244353;
inline int read() {
int x = 0, f = 1; char c = getchar();
while (c < '0' || c > '9') f = c == '-' ? -1 : 1, c = getchar();
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int n;
int siz[N], dep[N], dfn[N], nfd[N];
int seq[N * 2], p[N];
int st[N * 2][19], lg2[N * 2];
vector<int> T[N], del[N];
void dfs(int x, int fa) {
int lim = 1e9;
siz[x] = 1;
dep[x] = dep[fa] + 1;
nfd[dfn[x] = ++dfn[0]] = x;
seq[p[x] = ++seq[0]] = dfn[x];
for (auto son : T[x]) {
if (son == fa) continue;
dfs(son, x);
siz[x] += siz[son];
lim = min(lim, n - siz[son]);
seq[++seq[0]] = dfn[x];
}
lim = min(siz[x], lim);
del[lim].pb(x);
}
inline int dis(int x, int y) {
if (!x || !y) return -1;
int l = p[x], r = p[y];
if (l > r) swap(l, r);
int len = lg2[r - l + 1];
int lca = nfd[min(st[r][len], st[l + (1 << len) - 1][len])];
return dep[x] + dep[y] - 2 * dep[lca] + 1;
}
namespace segT {
struct node {
int len, u, v;
node() {}
node(int _u, int _v): u(_u), v(_v) { len = dis(u, v); }
} T[N << 2];
#define ls (p << 1)
#define rs (p << 1 | 1)
inline node max(const node &a, const node &b) {
return a.len < b.len ? b : a;
}
inline void push_up(int p) {
int lu = T[ls].u, lv = T[ls].v, ru = T[rs].u, rv = T[rs].v;
T[p] = max(max(max(max(max(T[ls], T[rs]),
node(lu, ru)), node(lu, rv)), node(lv, ru)), node(lv, rv));
}
void build(int p, int l, int r) {
if (l == r) {
T[p] = node(l, r);
return;
}
int mid = (l + r) >> 1;
build(ls, l, mid);
build(rs, mid + 1, r);
push_up(p);
}
void upd(int p, int l, int r, int gk) {
if (l == r) {
T[p] = node(0, 0);
return;
}
int mid = (l + r) >> 1;
if (mid >= gk) upd(ls, l, mid, gk);
else upd(rs, mid + 1, r, gk);
push_up(p);
}
#undef ls
#undef rs
}
int main() {
// freopen("island.in", "r", stdin);
// freopen("island.out", "w", stdout);
n = read();
for (int i = 1; i < n; i++) {
int u = read(), v = read();
T[u].pb(v), T[v].pb(u);
}
dfs(1, 0);
for (int i = 2; i <= seq[0]; i++) lg2[i] = lg2[i >> 1] + 1;
for (int i = 1; i <= seq[0]; i++) {
st[i][0] = seq[i];
for (int j = 1; j <= lg2[i]; j++)
st[i][j] = min(st[i][j - 1], st[i - (1 << j - 1)][j - 1]);
}
segT::build(1, 1, n);
for (int i = 1; i <= n; i++) {
if (i & 1) puts("1");
else {
printf("%d\n", max(1, segT::T[1].len));
for (int j : del[i >> 1]) segT::upd(1, 1, n, j);
}
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步