If it’s too hard to say goodbye

P17834

先观察到一个事实:如果能选择深度更浅的节点,则我们不会选择更深的,因为更浅的容易与其他节点聚合,且不影响答案。进而推出一个结论,一个半径 \(r\) 合法,当且仅当删去所有深度 \(\le r\) 的叶子,即剥去了 \(r\) 层叶子,能够存在一个选点方式,满足叶子全选且只有两个联通块。我们先定义剥去了 \(r\) 层叶子剩下的节点的集合为 \(G\)。

因此可以二分答案。定义 \(L\) 为需要删的点数。首先的,如果 \(|G| \le k\),则显然合法。否则,我们观察到,\(deg\ge 3\) 的节点一定要选择,否则必然分成三块。因此我们能够决策的只有度数为 2 的节点。进一步思考,意识到需要连着选择 \(L\) 个度数为 2 的节点。也就是说,需要找到一条 2 度链,满足存在一段长度 \(\ge L\) 的连续段,并且删去依然合法。则对于每一个起点,我们需要考虑其是否合法。但是,我们同时能发现,每一个节点对于起点的限制是一个区间,因此做个差分,做完了。

Code

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false);cin.tie(0),cout.tie(0)
#define File(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout)
#define LL long long
#define fi first
#define se second
const int N = 1e6 + 10;
int n,k;
int tim[N],deg[N];
bool vis[N];
vector<int> G[N];
void solve(){
	cin >> n >> k;
	for(int i=1;i<=n;i++){
		G[i].clear();
		deg[i] = 0;
	}
	for(int i=1;i<n;i++){
		int u,v; cin >> u >> v;
		G[u].push_back(v);
		G[v].push_back(u);
		deg[u] ++ ; deg[v] ++ ;
	}
	queue<int> q;
	for(int i=1;i<=n;i++){
		if(deg[i] == 1){
			tim[i] = 1;
			q.push(i);
		}
		else tim[i] = 0;
	}
	while(!q.empty()){
		int u = q.front(); q.pop();
		for(int v : G[u]){
			if(tim[v]) continue;
			deg[v]	 -- ;
			if(deg[v] == 1){ tim[v] = tim[u] + 1; q.push(v); }
		}
	}
	auto chk = [=](int r) -> bool{
		int cnt = 0;
		vector<int> deg(n+1,0);
		for(int u=1;u<=n;u++){
			if(tim[u] > r){
				cnt ++ ;
				for(int v : G[u]) if(tim[v] > r) deg[u] ++ ;
			}
		}
		if(cnt <= k) return 1;
		int siz = cnt - k;
		for(int u=1;u<=n;u++){
			if(deg[u] <= 0 || deg[u] == 2) continue;
			for(int v : G[u]){
				if(deg[v] == 2){
					int pre = u;
					vector<int> height;
					while(deg[v] == 2){
						deg[v] = -1;
						int mx = 0,to = -1;
						for(int w : G[v]){
							if(tim[w] <= r) mx = max(mx,tim[w]);
							else if(w != pre) to = w;
						}
						height.push_back(mx);
						pre = v; v = to; if(to == -1) break;
					}
					int m = height.size();
					if(m < siz) continue;
					int cnt = m - siz + 1; // ? 
					vector<int> sum(cnt+1,0);
					for(int i=0;i<m;i++){
						int a = r - height[i];
						int l = max(0,i + a - siz + 1);
						int r = min(cnt - 1,i - a);
						if(l <= r) sum[l] ++ , sum[r + 1] -- ;
					}
					for(int i=0;i<cnt;i++){
						if(i > 0) sum[i] += sum[i-1];
						if(!sum[i]) return 1;
					}
				}
			}
		}
		return 0;
	};
	int l = 0, r = n, fnd = 0;
	while(l <= r){
		int mid = l + r >> 1;
		if(chk(mid)){
			fnd = mid;
			r = mid - 1;
		}
		else l = mid + 1;
	}
	cout << fnd << "\n";
}
int main(){
	IOS;
	int c,T;
	cin >> c >> T;
	while(T -- )
		solve();
	return 0;
}
posted @ 2026-11-01 22:24  WinterXorSnow  阅读(3)  评论(0)    收藏  举报