树上二分。

Problem - D - Codeforces

 题意:

给定一个有n个节点的树,你可以尝试以下操作k次

选择树的一条边(v,u),使v是u的父级。删除边(v,u)。增加一条边(1,u)(即让u及其子树成为根的孩子)。

树的高度是其顶点的最大深度,而顶点的深度是根到它的路径上的边数。例如,顶点1的深度是0,因为它是根,而它所有的子节点的深度是1。

要求输出可以达到的最小的树的高度是多少?

输入
第一行包含一个整数t(1≤t≤104)--测试用例的数量。

每个测试案例的第一行包含两个整数n和k(2≤n≤2⋅105;0≤k≤n-1)--树中顶点的数量和你可以执行的最大操作数。

第二行包含n-1个整数p2,p3,...,pn(1≤pi<i)--第i个顶点的父级。顶点1是根。

所有测试案例的n之和不超过2⋅105。

输出
对于每个测试案例,打印一个整数--通过最多执行k次操作可以达到的最小的树的高度。

 本题求最大深度最小化问题,首先考虑二分做法

auto check = [&](int x)->bool
		{
			int cnt = 0;
            //由下往上
			for (int i = 1; i <= n; i++)depth[i] = 1;
			for (int i = n; i >= 2; i--)
			{
				for (auto t : e[i])
				{
					if (depth[t] == x)
					{
						cnt++; continue;
					}
					depth[i] = max(depth[i], depth[t] + 1);
				}
			}
			return cnt <= k;
		};
#include <bits/stdc++.h>
#define oo 0x3f3f3f3f
#define ll long long
#define IO ios::sync_with_stdio(0);cin.tie(0)
#define rep(i,a,n) for (ll i=a;i<=n;i++)
#define per(i,a,n) for (ll i=a;i>=n;i--)
#define fi first
#define se second
const int N = 2e5 + 10, M = 2 * N;
using namespace std;
typedef pair<ll, ll>  pll;
typedef pair<int, int> pii;
#define deb(i,x) if(int i==x) int k = 1; 
#define  pb push_back
#define YES {puts("YES");return;}
#define NO {puts("NO"); return;}
#define all(x) x.begin(),x.end()
ll lowbit(ll x) { return x & -x; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll qmi(ll a, ll b, ll mod) {
	ll res = 1; while (b) { if (b & 1) res = res * a % mod; a = a * a % mod; b >>= 1; }
	return res;
}

int mx = -oo;
vector<vector<int>> e(N);
void dfs(int p,int d)
{
	for (auto x : e[p])
	{
		if (e[x].empty())
			mx = max(mx, d + 1);
		dfs(x, d + 1);

	}
}
int depth[N];
int main()
{
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
#endif
	IO;
	int t; cin >> t;
	while (t--)
	{
		int n, k; cin >> n >> k;
		int v;
		mx = -oo;
		for (int i = 1; i <= n; i++)e[i].clear();
		for (int i = 2; i <= n; i++)
		{
			cin >> v;
			e[v].push_back(i);
		}
		dfs(1, 0);//得到最大深度,初始化r的范围
		auto check = [&](int x)->bool
		{
			int cnt = 0;
			for (int i = 1; i <= n; i++)depth[i] = 1;
			for (int i = n; i >= 2; i--)
			{
				for (auto t : e[i])
				{
					if (depth[t] == x)
					{
						cnt++; continue;
					}
					depth[i] = max(depth[i], depth[t] + 1);
				}
			}
			return cnt <= k;
		};
		int l = 1, r = mx;
		//if (n > 1)l = 1; 注意题目给定的n是大于等于2的,所以l初始化为1即可,不需考虑0
		while (l < r)
		{
			int mid = l + r >> 1;
			if (check(mid))
				r = mid;
			else
				l = mid + 1;
		}
		cout << l << endl;
	}

	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值