題目描述
題解
觀察題目性質,每一個節點的權值都是2的整次冪,性質爲:
這意味着我們需要優先選擇大的。
從大到小枚舉每一個節點,如果沒有被選取就將它到根路徑的每一個點都賦值爲.
觀察一下這樣的性質,發現:某一個點被覆蓋,其到根路徑上的所有點也會被覆蓋。
因此,我們用樹上倍增查找,不斷朝上向根節點跳躍,若被標記,則不跳;否則跳躍並記錄路經長度。
由於每一個點都會被覆蓋一次,所以只要部分的時間複雜度是的,但是基於倍增,複雜度變成了
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
int n, k;
int f[N][21], vis[N];
vector <int> a[N];
void dfs(int x,int fa)
{
for (int i=0;i<a[x].size();++i)
{
int y = a[x][i];
if (y == fa) continue;
f[y][0] = x;
dfs(y,x);
}
return;
}
void dp(void)
{
for (int j=1;j<=20;++j)
for (int i=1;i<=n;++i)
f[i][j] = f[f[i][j-1]][j-1];
return;
}
int find(int x)
{
int sum = 0;
for (int i=20;i>=0;--i)
if (!vis[f[x][i]])
sum += (1<<i), x = f[x][i];
return sum+1;
}
int main(void)
{
scanf("%d %d", &n, &k), k = n-k;
for (int i=1,x,y;i<n;++i)
{
scanf("%d %d", &x, &y);
a[x].push_back(y);
a[y].push_back(x);
}
dfs(n,0);
vis[0] = 1, dp();
for (int i=n;k>0;--i)
{
if (vis[i] == 1) continue;
int no_tag = find(i), now = i;
if (no_tag > k) continue;
while (vis[now] == 0)
k --, vis[now] = 1, now = f[now][0];
}
for (int i=1;i<=n;++i)
if (vis[i] == 0) printf("%d ", i);
return 0;
}