CF1039D You Are Given a Tree
考虑根号分治。
若
容易发现我们可以从树的叶子节点开始往上走,每走到一条合法的链就拼起来,贪心即可,复杂度
此外,我们还发现,令
同时,随着
于是我们考虑根号分治,假设阈值为
对于
此外,如果实现不好,可能会被卡,考虑将树上的点按照 dfn
序从大到小排,直接在序列上 DP,可以避免递归。
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n, blo, k;
vector<int> G[N];
pair<int, int> dp[N];
int fa[N];
int ans[N];
int e[N], h[N], ne[N], idx, ids;
struct Node
{
int u, id;
Node(int _u, int _i): u(_u), id(_i){}
Node(){}
bool operator<(const Node& g) const
{
return id > g.id;
}
}p[N];
void predfs(int u, int f)
{
p[u].u = u, p[u].id = ++ids;
fa[u] = f;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (j != f) predfs(j, u);
}
}
void dfs(int u, int fa)
{
dp[u] = make_pair(0, 1);
int maxn1 = -1, maxn2 = -1;
for (int i = h[u]; ~i; i = ne[i])
{
int j = e[i];
if (j == fa) continue;
dfs(j, u);
dp[u].first += dp[j].first;
if (dp[j].second > maxn1)
{
maxn2 = maxn1;
maxn1 = dp[j].second;
}
else if (dp[j].second > maxn2) maxn2 = dp[j].second;
}
if (maxn1 == -1 && maxn2 == -1)
{
return;
}
if (maxn2 == -1)
{
if (maxn1 + 1 >= k)
{
dp[u].first++, dp[u].second = 0;
}
else
{
dp[u].second += maxn1;
}
}
else
{
if (maxn1 + maxn2 + 1 >= k)
{
dp[u].first++;
dp[u].second = 0;
}
else
{
dp[u].second = maxn1 + 1;
}
}
}
int g[N];
int resse[N];
int m1[N], m2[N];
int getres(int x)
{
k = x;
for (int i = 1; i <= n; i++) m1[i] = m2[i] = -1, dp[i] = make_pair(0, 1);
for (int i = 1; i <= n; i++)
{
int u = p[i].u;
int maxn1 = m1[u], maxn2 = m2[u];
if (maxn1 == -1 && maxn2 == -1)
{
dp[fa[u]].first += dp[u].first;
if (dp[u].second > m1[fa[u]]) m2[fa[u]] = m1[fa[u]], m1[fa[u]] = dp[u].second;
else if (dp[u].second > m2[fa[u]]) m2[fa[u]] = dp[u].second;
continue;
}
if (maxn2 == -1)
{
if (maxn1 + 1 >= k)
{
dp[u].first++, dp[u].second = 0;
}
else
{
dp[u].second += maxn1;
}
}
else
{
if (maxn1 + maxn2 + 1 >= k)
{
dp[u].first++;
dp[u].second = 0;
}
else
{
dp[u].second = maxn1 + 1;
}
}
dp[fa[u]].first += dp[u].first;
if (dp[u].second > m1[fa[u]]) m2[fa[u]] = m1[fa[u]], m1[fa[u]] = dp[u].second;
else if (dp[u].second > m2[fa[u]]) m2[fa[u]] = dp[u].second;
}
return dp[1].first;
}
inline int read()
{
char ch(getchar());
int x(0);
while (ch < '0' || ch > '9')
{
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return x;
}
int main()
{
memset(h, -1, sizeof h);
memset(resse, -1, sizeof resse);
n = read();
for (int i = 1; i < n; i++)
{
int u, v;
u = read(), v = read();
e[idx] = v, ne[idx] = h[u], h[u] = idx++;
e[idx] = u, ne[idx] = h[v], h[v] = idx++;
}
predfs(1, 0);
sort(p + 1, p + n + 1);
blo = min(n, 230);
for (int i = 1; i <= blo; i++)
{
ans[i] = getres(i);
if (i == 1) ans[i] = n;
}
for (int j = 0; j <= n / blo; j++)
{
int l = blo + 1, r = n, res = 0;
while (l <= r)
{
int mid = l + r >> 1;
int gf;
if (resse[mid] != -1) gf = resse[mid];
else
{
gf = resse[mid] = getres(mid);
}
if (gf >= j)
{
res = mid;
l = mid + 1;
}
else r = mid - 1;
}
g[j] = res;
}
for (int i = 0; i <= n / blo; i++)
{
if (g[i] && !g[i + 1])
{
for (int j = blo + 1; j <= g[i]; j++) ans[j] = i;
break;
}
else if (!g[i]) break;
else
{
if (g[i] == g[i + 1]) continue;
for (int j = g[i + 1] + 1; j <= g[i]; j++) ans[j] = i;
}
}
for (int i = 1; i <= n; i++) printf("%d\n", ans[i]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现