[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;
}