题意:n个点的树.n<=2e5.最少添加多少条边才能使得节点1到任意一个点的距离<=2.
首先最多不会添加超过n-2条边.
假如1->v,1->u的距离超过2.(u,v)之前没有边
添加(1,u),(u,v) 显然把(u,v)边替换成(1,v)结果更优.所以添加的每条边都是1的出边.
考虑的角度很重要.一个节点u可以连它的fa,本身,son[u].情况比较多.
把距离节点1超过2的节点压入set.考虑离1最远的那个节点v.
添加的边只有两种.要么是(1,v),要么是(1,fa[v]).
若加(1,v) 则set中能去掉(v,fa[v]).
若加(1,fa[v]) 则set中去掉(v,fa[v])以及所有和fa[v]相邻的节点.
重复以上策略即可 O(nlogn).
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> ii;
const int N=2e5+5;
int n,fa[N],d[N];
set<ii> s;
vector<int> e[N];
void dfs(int u,int par,int dis){
if(dis>2) s.insert(ii(dis,u));
d[u]=dis;
for(int i=0;i<e[u].size();i++){
int v=e[u][i];
if(v==par) continue;
fa[v]=u;
dfs(v,u,dis+1);
}
}
void solve(){
int res=0;
while(!s.empty()){
ii t=*s.rbegin();
int v=t.second;
int u=fa[v];
if(s.find(ii(d[u],u))!=s.end()) s.erase(ii(d[u],u));
for(int i=0;i<e[u].size();i++){
int x=e[u][i];
if(s.find(ii(d[x],x))!=s.end())//O(nlogn).
s.erase(ii(d[x],x));
}
res++;
}
cout<<res<<'\n';
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
cin>>n;
int u,v;
for(int i=1;i<=n-1;i++){
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
dfs(1,0,0);
solve();
return 0;
}