CF1039D You Are Given a Tree

考虑根号分治。

kk 固定,怎么做?

容易发现我们可以从树的叶子节点开始往上走,每走到一条合法的链就拼起来,贪心即可,复杂度 O(n)O(n)

此外,我们还发现,令 ansians_ik=ik=i 时的答案,那么由于每条链不相交,所以 ansinkans_i \leq \lfloor \dfrac{n}{k}\rfloor

同时,随着 ii 增大,ansians_i 单调不增。

于是我们考虑根号分治,假设阈值为 pp,对于 1ip1 \leq i \leq p,我们暴力计算,复杂度 O(np)O(np)

对于 p<inp < i \leq n,其不同的答案个数的量级为 O(np)O(\dfrac{n}{p}),由于单调性,所以每个答案的出现位置都是一个区间,可以做到 O(nplogn×n)=O(n2lognp)O(\dfrac{n}{p} \log n \times n) = O(\dfrac{n^2 \log n}{p})。当 p=nlognp = \sqrt{n \log n} 时取得最小值。当然直接取 n\sqrt{n} 或者类似的常数也可以。

此外,如果实现不好,可能会被卡,考虑将树上的点按照 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;
}
posted @   HappyBobb  阅读(2)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示