[CodeForces][贪心][树形结构]CF980E The Number Games

题面

看到树形结构我们可能会想到树形 \(DP\),但是仔细研究过后我们会发现这题是不需要 \(DP\) 的。
观察每个点的贡献为 \(2^i\) ( \(i\) 为节点标号 ),也就是说,选择一个标号更大的点一定比选择几个标号小的点优

这时我们贪心的思路就明朗了:
\(n\ -\ 1\) 遍历每个点,被选择的点之间的路径都需要连接,只要一个点路径上需要新增的点加起来不超过限制,就加入该点 ( 以及路径上的点 )。

至于路径,我们只需要用 \(DFS\) 序维护,然后用差分树状数组暴力维护一下一个点到根节点的路径长度即可

代码:

// 贪心的考虑:2^n 一定保留
// DFS 暴力跳点

# include <iostream>
# include <cstdio>
# define MAXN 1000005
# define fa(x) nd[x].fa
# define dep(x) nd[x].dep
# define siz(x) nd[x].siz
# define dfn(x) nd[x].dfn

struct edge{
	int v, next;
}e[MAXN<<1];
struct node{
	int fa, dep, siz, dfn;
}nd[MAXN];
int cntS, lim;
int hd[MAXN], cntE, bit[MAXN];
bool sel[MAXN]; int cntSel;

void AddE(int u, int v);
void DFS(int now, int fa);
int Lowbit(int x);
void Update(int pos, int val);
int GetSum(int pos); 

int main(){
	int n, k;

	scanf("%d%d", &n, &k);

	k = n - k; // 删除点数转化为保留点数

	lim = n;

	for(int i = 1, u, v; i <= n-1; i++){
		scanf("%d%d", &u, &v);
		AddE(u, v); AddE(v, u);
	}


	DFS(n, 0);
	sel[n] = 1;

	for(int i = n; i >= 1; i--){
		if((!sel[i]) && (dep(i)-GetSum(dfn(i))+cntSel  <= k)){ // 要求选择当前点后新增点数不超过总保留点数
			for(int now = i; now != n; now = fa(now)){
				if(sel[now]){
					break;
				}
				else{
					sel[now] = 1;
					Update(dfn(now), 1); Update(dfn(now)+siz(now), -1); // 差分
					cntSel++;
				}
			}
		}
	}

	for(int i = 1; i <= n; i++){
		if(!sel[i]){
			printf("%d ", i);
		}
	}

	return 0;
}

int GetSum(int pos){
	int ans = 0;
	while(pos){
		ans += bit[pos];
		pos -= Lowbit(pos);
	}
	return ans;
}

void Update(int pos, int val){
	while(pos <= lim){
		bit[pos] += val;
		pos += Lowbit(pos);
	}
}

int Lowbit(int x){
	return x & (-x);
}

void DFS(int now, int fa){
	fa(now) = fa, dep(now) = dep(fa) + 1, siz(now) = 1, dfn(now) = ++cntS;

	for(int i = hd[now]; i; i = e[i].next){
		if(e[i].v == fa){
			continue;
		}

		DFS(e[i].v, now);
		siz(now) += siz(e[i].v);
	}
}

void AddE(int u, int v){
	e[++cntE] = (edge){v, hd[u]};
	hd[u] = cntE;
}
posted @ 2020-09-08 20:16  ChPu437  阅读(133)  评论(0编辑  收藏  举报