Codeforces Round 975 (Div. 1) C Solution
闲话
因为累所以没打,感觉有点亏。
题解
该解法与官方解法不同。
对于一个固定的深度,为了仅保留对应深度的叶子,显然需要进行以下两种操作:
- 删除所有深度大于指定深度的节点。
- 删除所有深度小于指定深度的叶子。
对于任意一个深度为 \(d\) 的叶子,将其删除后有两种可能:
- 若其父节点只有一个儿子,即该节点,则删除后会新增一个深度为 \((d-1)\) 的叶子。
- 若其父节点不止一个儿子,则删除后不会产生新的叶子。
对于上方提到的操作,操作 \(2\) 的操作次数可以简单地使用后缀和计算。考虑如何计算操作 \(1\) 所用的操作次数。
答案是,我们可以直接暴力删除。
我们按升序枚举所有可能的最终固定深度,对于上一次枚举的最终固定深度上的所有叶子节点,我们显然需要不断地删去它们直到出现上述提到的两种可能中的第 \(2\) 种,即不产生新的叶子。这一点可以简单地通过存储每一个节点的度数和父节点实现。
由于每个节点最多会被删去一次,那么设节点的最大深度为 \(D\),则总时间复杂度为 \(O(n+D)\)。
代码
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
const int N = 5e5 + 10;
int n, idx, deg[N], f[N], dep[N], dpc[N], res, prc;
vector<int> road[N], dpv[N];
void dfs(int x)
{
idx = max(idx, dep[x]);
dpc[dep[x]]++;
if (road[x].size() == 1)
{
dpv[dep[x]].emplace_back(x);
return;
}
for (auto &i : road[x])
{
if (i == f[x])
continue;
f[i] = x;
dep[i] = dep[x] + 1;
dfs(i);
}
}
void prune(int x)
{
do
{
x = f[x];
deg[x]--;
prc++;
} while (deg[x] == 1);
}
void run()
{
scanf("%d", &n);
memset(deg + 1, 0, n << 2);
memset(dpc + 1, 0, idx << 2);
for (int i = 1; i <= n; i++)
{
road[i].clear();
}
for (int i = 1; i <= idx; i++)
{
dpv[i].clear();
}
idx = 0;
for (int i = 1, x, y; i < n; i++)
{
scanf("%d%d", &x, &y);
deg[x]++, deg[y]++;
road[x].emplace_back(y);
road[y].emplace_back(x);
}
deg[1] = 0;
for (auto &i : road[1])
{
dep[i] = 1;
f[i] = 1;
dfs(i);
}
for (int i = idx - 1; i; i--)
{
dpc[i] += dpc[i + 1];
}
dpc[idx + 1] = 0;
res = n - 1;
prc = 0;
for (int i = 1; i <= idx; i++)
{
for (auto &j : dpv[i - 1])
{
prune(j);
}
res = min(res, prc + dpc[i + 1]);
}
printf("%d\n", res);
}
int main()
{
int T = 1;
scanf("%d", &T);
while (T--)
run();
}