[説明] CJOI2019ニワトリ泥棒ツリー(構造タイトル)
鶏泥棒\(N- \)ツリーノード。
鶏泥棒は、このツリーは、各エッジはエッジをカットしているため、任意の泥棒はこの木もしないことができます木のボール側を盗んだ、あまりにも安全ではないと思う
渡します。この木を保護するために、鶏泥棒は木が無重力エッジになるように、いくつかのエッジを追加し、リングの自己ビュー、およびいずれかの側を削除することを決定し
、それはまだユニコム後。
しかし、各エッジを追加すること、料金を持っています。鶏泥棒がプレイステーション4を購入するのに十分なお金を節約したいと思いますので、彼は知りたいと思った、少なくとも追加する
エッジの数を加えたこと?もちろん、単に数を知ることは十分ではありません、いくつかのテストポイントでは、彼はエッジを追加する方法を知りたいと思いました。
答えは明らかに最適下限である\(\ lfloor \ dfrac {C} + 1 2 \ rfloorの\)、\は(C \)リーフノードの数である(ルートの程度を保証しません)。(考えてみましょう:葉ノードの親側を停止します)
このような構成方法を考慮し、リーフノードは、そのに接続されている\(LCA \)別のルートノードであり、残りの1点があっても、ルートに追加されます。それは、各サブツリーのリーフノード内になるよう未満で、ルートノードを見つける\(\ lfloor \ dfrac {C + 1} 2 \ rfloorの\)
直接要求\(DFS \)配列、得られた葉を添加したアレイ(\ \ VE) 、\は、(\ [I] VE)と\(VEの[iが\ lfloor + \ \ dfrac {C + 1} 2 \ rfloor]を)接続、ルートノードへの余分な接続の場合。
明らかに\(VEの[i]が\)と\(VEの[I + \ lfloor \ dfrac {C + 1} 2 \ rfloor] \) \(LCAが\)ルートです。
//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std; typedef long long ll;
inline int qr(){
register int ret=0,f=0;
register char c=getchar();
while(c<48||c>57)f|=c==45,c=getchar();
while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
return f?-ret:ret;
}
const int maxn=5e5+5;
int dr[maxn];
struct E{
int to,nx;
E(){to=nx=0;}
E(const int&a,const int&b){to=a;nx=b;}
}e[maxn<<1];
vector<int> ve;
int head[maxn];
int n,rt,ans,op,cnt;
inline void add(const int&fr,const int&to,const int&b){
e[++cnt]=E(to,head[fr]);
head[fr]=cnt;
++dr[fr];
if(dr[fr]>1)rt=fr;
if(b)add(to,fr,0);
}
void dfs(const int&now,const int&last){
if(dr[now]==1) ve.push_back(now);
for(register int t=head[now];t;t=e[t].nx)
if(e[t].to!=last) dfs(e[t].to,now);
}
int main(){
//freopen("tree.in","r",stdin);
//freopen("tree.out","w",stdout);
n=qr();op=qr();
for(register int t=1;t<n;++t) add(qr(),qr(),1);
dfs(rt,0);
int sz=ve.size();
printf("%d\n",(sz+1)>>1);
if(!op)return 0;
for(register int t=0;t<(sz>>1);++t)
printf("%d %d\n",ve[t],ve[t+(sz>>1)]);
if(sz&1) printf("%d %d",ve[sz-1],rt);
return 0;
}