Tree Cutting Codeforces Round 936 (Div. 2) 1946C

Problem - C - Codeforces

题目大意:有一棵n个点的树,要求找到一个最大的x,使得在树中移除恰好k个边后,树中剩余的每一部分都至少有x个点。

1<=k<n<1e5

思路:因为总的点数是n,是定值,所以x越大,那么按最优方法操作移除的边数就越少,反之x越小,需要移除的边数就越多,很明显可以利用这个单调性二分x。

那么接下来考虑在确定了某个x的情况下求需要移除的最少边数,因为按照上面的单调性,移除的边数越少x越大。

那么很容易想到的就是进行dfs,后序维护当前点u的子树大小siz[u],如果siz[u]>=x,那么这个点和它父节点之间的边就要断掉,使用的边的数量tot+1,然后令siz[u]=0,使其不影响后序的dfs过程,但如果当前点时根节点也就是1号点,它没有父节点,所以tot不能+1,而且如果以1为根的子树的大小<x,那么之前断掉的一个边还要连回去,使得该子树大小满足>=x。

如果得到的tot>=k,那么说明x还可增大,反之要减小x

#include<__msvc_all_public_headers.hpp>
//#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
typedef long long ll;
const ll MOD = 1e9 + 7;
ll n;
vector<int>g[N];
int siz[N];
ll k;
int tot = 0;
ll qpow(ll a, ll b)
{//快速幂
	a %= MOD;
	ll ret = 1;
	while (b)
	{
		if (b & 1)
		{
			ret = ret * a % MOD;
		}
		a = a * a % MOD;
		b >>= 1;
	}
	return ret;
}
void init()
{
	for (int i = 1; i <= n; i++)
	{
		g[i].clear();
		siz[i] = 1;
	}
}
void dfs(int u, int fa, int x)
{
	for (const auto& i:g[u])
	{
		int v = g[u][i];
		if (v == fa)
		{
			continue;
		}
		dfs(v, u, x);
		siz[u] += siz[v];//维护子树大小
	}
	if (u != 1 && siz[u] >= x)
	{//当前子树的点数>=x,可以断开父边
		tot++;
		siz[u] = 0;
	}
	if (u == 1 && siz[u] < x)
	{//以1为根且子树大小<x,要连回去一条边
		tot--;
	}
}
void solve()
{
	cin >> n;
	init();
	cin >> k;
	for (int i = 1; i < n; i++)
	{
		int u, v;
		cin >> u >> v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	int l = 1, r = n, mid;
	int ans;
	while (l <= r)
	{//二分x
		mid = (l + r) >> 1;
		tot = 0;
		for (int i = 1; i <= n; i++)
		{
			siz[i] = 1;
		}
		dfs(1, 0, mid);
		if (tot >= k)
		{
			ans = mid;
			l = mid + 1;
		}
		else
			r = mid - 1;
	}
	cout << ans;
	cout << '\n';
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t;
	cin >> t;
	//t = 1;
	while (t--)
	{
		solve();
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

timidcatt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值