CF 506E Tree with Small Distances 思维.

题意: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;
}

猜你喜欢

转载自blog.csdn.net/noone0/article/details/82112284