bzoj 2067 [Poi2004]SZN——二分+贪心
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2067
最少的线段可以贪心地想出来。(结果还是写错了)就是偶数孩子可以自己配对,奇数孩子要带一个上去;算条数的时候在该条拐弯或截止的时候算,就是每个点的度数减1除以2求和,最后加上1表示根节点。
还以为第二问能贪心做呢。结果WA。奇数孩子带一个最小的上去是不行的;偶数孩子都不带上去也是不行的。
于是二分一下答案。结果WA。偶数孩子带一个尽量小的上去还能贪心,奇数孩子并不是用中间那个孩子与别的孩子配对使得自己带一个尽量小的孩子上去。
奇数孩子不知带哪个孩子上去,像这种就应该考虑枚举、二分之类暴力一点的做法。枚举的话可能n^2,所以二分一下,判断就是去掉这个孩子,剩下的贪心配对。
顺便把偶数孩子的那个“带0上去”也作为一个孩子加进去,就很方便了。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1e4+5; int n,hd[N],xnt,to[N<<1],nxt[N<<1],rt,l,r,mid; int deg[N],dis[N],ans,sta[N],top; bool flag; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9') ret=(ret<<3)+(ret<<1)+ch-'0',ch=getchar(); return fx?ret:-ret; } void add(int x,int y) { to[++xnt]=y; nxt[xnt]=hd[x]; hd[x]=xnt; to[++xnt]=x; nxt[xnt]=hd[y]; hd[y]=xnt; deg[x]++; deg[y]++; } void dfs(int cr,int fa) { for(int i=hd[cr],v;i;i=nxt[i]) if((v=to[i])!=fa) { dfs(v,cr); if(flag)return; } top=0; for(int i=hd[cr];i;i=nxt[i]) if(to[i]!=fa) sta[++top]=dis[to[i]]+1; if(deg[cr]&1) sta[++top]=0;//偶数孩子可以0地向上 sort(sta+1,sta+top+1); if(cr==1) { for(int i=1,j=top;i<j;i++,j--) if(sta[i]+sta[j]>mid) {flag=1;return;} return; } int tl=1,tr=top,ret=-1; while(tl<=tr) { int tmid=tl+tr>>1; bool fg=0; for(int i=1,j=top;i<j;i++,j--) { if(i==tmid) i++; if(j==tmid) j--; if(sta[i]+sta[j]>mid){fg=1;break;} } if(!fg) ret=tmid,tr=tmid-1; else tl=tmid+1; } if(ret==-1)flag=1; else dis[cr]=sta[ret]; } int main() { n=rdn(); for(int i=1,u,v;i<n;i++) { u=rdn(); v=rdn(); add(u,v); } // for(int i=2;i<=n;i++) ans+=deg[i]>>1; // ans+=(deg[1]==1?0:(deg[1]+1)>>1); for(int i=1;i<=n;i++) ans+=((deg[i]-1)>>1); printf("%d ",ans+1); r=n; while(l<=r) { mid=l+r>>1; flag=0; dfs(1,0); if(!flag) ans=mid,r=mid-1; else l=mid+1; } printf("%d\n",ans); return 0; }