If it’s too hard to say goodbye
先观察到一个事实:如果能选择深度更浅的节点,则我们不会选择更深的,因为更浅的容易与其他节点聚合,且不影响答案。进而推出一个结论,一个半径 \(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;
}

浙公网安备 33010602011771号