『貪心·樹上倍增』CF980E The Number Games

題目描述

在這裏插入圖片描述

題解

觀察題目性質,每一個節點的權值都是2的整次冪,性質爲:2k>i=1k12i2^k>\sum_{i=1}^{k-1}2^i

這意味着我們需要優先選擇大的。

從大到小枚舉每一個節點,如果沒有被選取就將它到根路徑的每一個點都賦值爲11.

觀察一下這樣的性質,發現:某一個點被覆蓋,其到根路徑上的所有點也會被覆蓋。

因此,我們用樹上倍增查找,不斷朝上向根節點跳躍,若被標記,則不跳;否則跳躍並記錄路經長度。

由於每一個點都會被覆蓋一次,所以只要部分的時間複雜度是O(n)O(n)的,但是基於倍增,複雜度變成了O(nlogn)O(nlogn)

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章