@ysner
2018-09-21T08:13:28.000000Z
字数 2073
阅读 2402
贪心 二分
给一棵树,问最少要用多少条不相交的链,才能覆盖整棵树;并在此基础上,最小化其中最长链的长度。
第一问直接贪心即可。
若该点有个儿子,则有条链。我们可以把这条链两两配对,若有多出的链,就延伸到该点父亲去。则形成条链(在链的最高点统计个数)。
而根节点号没有父亲,所以形成条链。
“最大值的最小值”问题显然需要二分。
先二分答案。
设为从下往上延伸到点的链的长度。
然后在每个点中,把儿子中最长链与最短链合并,次长链和次短链合并。。。尽量使所有链长符合要求。
在符合要求的基础上,使向上延伸的链的长度尽量短。
这里又可以二分,二分那条向上延伸的链。
但是要注意一点,
存在一种情况:某点有偶数个儿子时,不一定要把条链一一配对完毕;一条链不配对,另一条链向上延伸也是可以的。
复杂度。
有些小细节。(看打了标记的行)
#include<iostream>#include<cstring>#include<cmath>#include<cstdio>#include<cstdlib>#include<algorithm>#include<vector>#define ll long long#define re register#define il inline#define pb(a) push_back(a)#define fp(i,a,b) for(re int i=a;i<=b;i++)#define fq(i,a,b) for(re int i=a;i>=b;i--)using namespace std;const int N=1e5+100;int n,h[N],cnt,in[N],ans,f[N],tot;struct Edge{int to,nxt;}e[N<<1];vector<int>son[N];il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;}il ll gi(){re ll x=0,t=1;re char ch=getchar();while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();if(ch=='-') t=-1,ch=getchar();while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();return x*t;}il int calc(re int u,re int p,re int w){re int l=-1,r=tot;while(l<r){++l;--r;if(l==p) ++l;if(r==p) --r;if(son[u][l]+son[u][r]>w) return 0;}return 1;}il int dfs(re int u,re int fa,re int w){for(re int i=h[u];i+1;i=e[i].nxt){re int v=e[i].to;if(v==fa) continue;if(!dfs(v,u,w)) return 0;//son[u].pb(f[v]+1);}if(!son[u].size()) {f[u]=0;return 1;}//if(u!=1&&son[u].size()%2==0) son[u].pb(0);sort(son[u].begin(),son[u].end());tot=son[u].size();if(u==1&&tot%2==0){f[u]=0;if(!calc(u,-10,w)) return 0;}else{re int l=0,r=tot-1,p=-1;while(l<=r){re int mid=l+r>>1;if(calc(u,mid,w)) p=mid,r=mid-1;else l=mid+1;}if(p==-1) return 0;f[u]=son[u][p];}return f[u]<=w;//}il int check(re int x){fp(i,1,n) son[i].clear(),f[i]=0;return dfs(1,0,x);}int main(){freopen("2067.in","r",stdin);freopen("2067.out","w",stdout);memset(h,-1,sizeof(h));n=gi();fp(i,1,n-1){re int u=gi(),v=gi();add(u,v);add(v,u);++in[u];++in[v];}ans+=(in[1]+1)/2;fp(i,2,n) ans+=(in[i]-1)/2;printf("%d ",ans);re int l=1,r=n,ans=1;while(l<=r){re int mid=l+r>>1;if(check(mid)) ans=mid,r=mid-1;else l=mid+1;}printf("%d\n",ans);return 0;}
