1. 程式人生 > >bzoj 2067 [ Poi 2004 ] SZN —— 二分

bzoj 2067 [ Poi 2004 ] SZN —— 二分

esp style using com fin main 自己 while 滿足

題目:https://www.lydsy.com/JudgeOnline/problem.php?id=2067

問題1:貪心考慮,應該是每個點的兒子盡量兩兩配對,如果剩一個就和自己合並向上,所以 ans = 1 + ∑(1<= i <= n ) (deg[i] - 1)/2

問題2:二分最長線段的長度,設 f[x] 表示自己帶著的鏈的長度(即兒子中剩下的那一個帶來的長度),判斷是否滿足條件即可;

如果當前節點有偶數個兒子,那麽加一個 f 值為0的,進行二分;

註意根要單獨判斷,因為不能向上帶了。

代碼如下:

#include<iostream>
#include<cstdio>
#include
<cstring> #include<algorithm> #define mid ((l+r)>>1) using namespace std; int const xn=10005; int n,hd[xn],ct,to[xn<<1],nxt[xn<<1],f[xn],ans,deg[xn],a[xn],cnt; void add(int x,int y){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct;} int rd() { int ret=0,f=1; char ch=getchar(); while
(ch<0||ch>9){if(ch==-)f=0; ch=getchar();} while(ch>=0&&ch<=9)ret=(ret<<3)+(ret<<1)+ch-0,ch=getchar(); return f?ret:-ret; } bool ck2(int pos,int lim) { for(int l=1,r=cnt;l<=r;l++,r--) { if(l==pos)l++; if(r==pos)r--; if
(a[l]+a[r]>lim)return 0; } return 1; } bool ck(int x,int fa,int lim) { for(int i=hd[x],u;i;i=nxt[i]) { if((u=to[i])==fa)continue; if(!ck(u,x,lim))return 0; } cnt=0; for(int i=hd[x];i;i=nxt[i])if(to[i]!=fa)a[++cnt]=f[to[i]]+1; if(x==1) { sort(a+1,a+cnt+1); if(cnt%2)f[x]=a[cnt],cnt--; for(int l=1,r=cnt;l<=r;l++,r--) if(a[l]+a[r]>lim)return 0; return f[x]<=lim; } if(cnt%2==0)a[++cnt]=0; sort(a+1,a+cnt+1); int l=1,r=cnt,ret=-1; while(l<=r) { if(ck2(mid,lim))ret=mid,r=mid-1; else l=mid+1; } if(ret==-1)return 0; f[x]=a[ret]; return f[x]<=lim; } int main() { n=rd(); int ans1=1; for(int i=1,x,y;i<n;i++) { x=rd(); y=rd(); deg[x]++; deg[y]++; add(x,y); add(y,x); } for(int i=1;i<=n;i++)ans1+=(deg[i]-1)/2; printf("%d ",ans1); int l=0,r=n; while(l<=r) { if(ck(1,0,mid))ans=mid,r=mid-1; else l=mid+1; } printf("%d\n",ans); return 0; }

bzoj 2067 [ Poi 2004 ] SZN —— 二分