「BalticOI 2015」网络

题意简述:给你一颗\(n\)个点的树,问你至少需要加几条边,可以使得这棵树删去任意一条边后依然联通。并给出一种方案。

很显然可以转化为对于树的任意一颗子树,都需要连一条边到其他子树,显然连叶子节点是最优的。

考虑这样一种连边方案:设一共\(k\)个叶子节点,按\(dfs\)序排序,第\(i\)个叶子节点连向第\(i+\lceil\frac{k}{2}\rceil\)个叶子节点,容易证明这样连边一定不会存在一颗子树内没有边连向其他子树。

代码:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<queue>
#include<set>
using namespace std;
#define rg register
void read(int &x){
    char ch;bool ok;
    for(ok=0,ch=getchar();!isdigit(ch);ch=getchar())if(ch=='-')ok=1;
    for(x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());if(ok)x=-x;
}
const int maxn=5e5+10;
int n,m,in[maxn],rt,dfn[maxn],id,k,w[maxn],nid[maxn];
int pre[maxn*2],nxt[maxn*2],h[maxn],cnt;
void ins(int x,int y){
    pre[++cnt]=y,nxt[cnt]=h[x],h[x]=cnt;
    pre[++cnt]=x,nxt[cnt]=h[y],h[y]=cnt;
}
void dfs(int x,int fa){
    dfn[x]=++id;nid[id]=x;
    for(rg int i=h[x];i;i=nxt[i])
	if(pre[i]!=fa)dfs(pre[i],x);
    if(in[x]==1)w[++k]=dfn[x];
}
int main(){
    read(n);
    for(rg int i=1,x,y;i<n;i++)read(x),read(y),ins(x,y),in[x]++,in[y]++;
    for(rg int i=1;i<=n;i++)if(in[i]!=1){rt=i;break;}
    dfs(rt,0);printf("%d\n",(k+1)/2);
    for(rg int i=1;i<=k/2;i++)printf("%d %d\n",nid[w[i]],nid[w[i+(k+1)/2]]);
    if(k&1)printf("%d %d\n",nid[w[k/2+1]],rt);
}
posted @ 2019-11-14 15:26  蒟蒻--lichenxi  阅读(254)  评论(0编辑  收藏  举报