这题一看就让人想到拓扑排序,所以我们自然想到用队列来搞
这题中队列可以存放一次操作后将被删除的点
最开始出入度是0或1的点会被删除,就把他们放到队列中。注意把进过队列的点标记为true,这样做是为了保证进过队列的点不会再进,即删过的点不会再删,或者说不会删一个点多次
一次操作中把队列的点都遍历一遍,遍历每个点的边,如果此边连接的点入过队列,就直接continue,否则该点有可能是新叶子结点,我们要把边删掉,那么这个点的出入度减1,如果这个点的出入度边为了0或1,那就说明它的新叶子结点,把它放入队列中
#include<iostream>
#include<cmath>
#include<map>
#include<cstring>
#include<queue>
#include<algorithm>
#include<string>
#include<vector>
#include<stack>
#include<sstream>
#include<set>
#include<stack>
#include<cstdio>
using namespace std;
typedef long long ll;
int to[400010];
vector<int> g[400010];
bool vis[400010];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int t;
cin >> t;
while (t--)
{
int n, k;
cin >> n >> k;
int cnt = n;
for (int i = 1;i <= n;i++)
{
g[i].clear();
vis[i] = 0;to[i] = 0;
}
for (int i = 1;i < n;i++)
{
int u, v;
cin >> u >> v;
g[u].push_back(v);
to[u]++, to[v]++;
g[v].push_back(u);
}
queue<int> q;
for (int i = 1;i <= n;i++)
if (to[i] == 0 || to[i] == 1) {
q.push(i);
vis[i] = true;
}
while (k--&&cnt)
{
cnt -= q.size();
int z = q.size();
while (z--)
{
int t = q.front();
q.pop();
for (int i = 0;i < g[t].size();i++)
{
if (vis[g[t][i]]) continue;
to[g[t][i]]--;
if ((to[g[t][i]] == 0 || to[g[t][i]] == 1))
{
q.push(g[t][i]);
vis[g[t][i]] = true;
}
}
}
}
cout << cnt << endl;
}
}