D. Reset K Edges(dfs/bfs/二分/贪心)

题目
参考

题意

给定一个根为1的一棵树,节点为1到n。现可以执行以下操作

  • 选择一条边(u,v)
  • 删除边(u,v)
  • 建立新边(1,v)

最多可以执行上述操作k次,问最多可以将树砍到多高。树的高度定义为其最深的叶子节点。根节点的深度为0。

思路

二分高度,计算高度为d时,最少需要操作多少次。
对于高度为d,从深度高的叶子节点开始遍历,如果当前节点深度大于d,则用贪心策略,将该节点的第d代祖先的边砍掉,接到根节点上。

如何遍历深度高的叶子节点遍历,用bfs预处理,储存它们的遍历顺序。
如何记录节点的第d代祖先,用dfs预处理。

详见代码

代码

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define pcc pair<char, char>
#define inf 0x3f3f3f3f
const int maxn = 200010;

int n, k, p;
vector<vector<int> > g;
vector<int> st;
int pt[maxn], h[maxn];
vector<int> ord;
bool vis[maxn];
void dfs(int u, int d) {
	st.push_back(u);
	if (st.size() >= d) {
		pt[u] = st[st.size()-d];
	}
	for (auto v: g[u]) {
		dfs(v, d);
	}
	st.pop_back();
}
void dfs2(int u) {
	vis[u] = 1;
	for (auto v: g[u]) {
		if (!vis[v]) {
			dfs2(v);
		}
	}
}
int cal(int d) {
	// 1. cal the ancestor with distance d.
	st.clear();
	memset(pt, -1, sizeof(pt));
	dfs(0, d);
	
	// 2. calculate the depth of every node,
	// and bfs order.
	ord.clear();
	queue<int> q;
	h[0] = 0;
	q.push(0);
	while (!q.empty()) {
		int u = q.front();
		q.pop();
		ord.push_back(u);
		for (auto v: g[u]) {
			q.push(v);
			h[v] = h[u] + 1;
		}
	}
	reverse(ord.begin(), ord.end());
	
	// 3. calculate the minist number to keep 
	// the tree hight <= d.
	int res = 0;
	memset(vis, 0, sizeof(vis));
	for (auto u: ord) {
		if (vis[u] || h[u] <= d) {
			continue;
		}
		++res;
		dfs2(pt[u]);
	}
	
	return res;
}
int binary_search() {
	int l = 1, r = n - 1;
	int ans = n;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (cal(mid) <= k) {
			ans = mid;
			r = mid - 1;
		} else {
			l = mid + 1;
		}
	}
	return ans;
}
void solve() {
    scanf("%d%d", &n, &k);
    g.assign(n, vector<int>());
//    g.resize(n, vector<int>()); // use resize, can't clear elements! 
    for (int i = 1; i < n; ++i) {
    	scanf("%d", &p);
    	--p;
    	g[p].push_back(i);
	}
	printf("%d\n", binary_search());
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
    	solve();
	}
}
/*
99
4 3
1 1 1
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值