bzoj 2067: [Poi2004]SZN【贪心+二分+树形dp】

时间:2024-04-29 15:37:38

第一问就是Σ(deg[u]-1)/2+1

第二问是二分,判断的时候考虑第一问的贪心规则,对于奇度数的点,两两配对之后一条延伸到上面;对于欧度数的点,两两配对或者deg[u]-2的点配对,然后一条断在这个点,一条延伸上去,按这个树形dp判断一下即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=10005;
int n,h[N],cnt,ans=1,d[N],f[N],fl,a[N],tot;
struct qwe
{
int ne,to;
}e[N<<1];
int read()
{
int r=0,f=1;
char p=getchar();
while(p>'9'||p<'0')
{
if(p=='-')
f=-1;
p=getchar();
}
while(p>='0'&&p<='9')
{
r=r*10+p-48;
p=getchar();
}
return r*f;
}
void add(int u,int v)
{
cnt++;
e[cnt].ne=h[u];
e[cnt].to=v;
d[v]++;
h[u]=cnt;
}
bool pd(int mid,int w)
{
int l=1,r=tot;
while(l<r)
{
if(l==mid)
l++;
if(r==mid)
r--;
if(a[l]+a[r]>w)
return 0;
l++,r--;
}
return 1;
}
void dfs(int u,int fa,int w)
{
if(!fl)
return;
for(int i=h[u];i;i=e[i].ne)
if(e[i].to!=fa)
dfs(e[i].to,u,w);
f[u]=0,tot=0;
for(int i=h[u];i;i=e[i].ne)
if(e[i].to!=fa)
a[++tot]=f[e[i].to]+1;
sort(a+1,a+1+tot);
if(!tot)
return;
if(a[tot]>w)
{
fl=0;
return;
}
if(!(tot&1))
{
for(int i=1;i<=tot/2;i++)
if(a[i]+a[tot-i+1]>w)
{
if(u==1)
{
fl=0;
return;
}
else
{
tot--;
break;
}
}
}
if(tot&1)
{
int l=1,r=tot,ans=tot+1;
while(l<=r)
{
int mid=(l+r)>>1;
if(pd(mid,w))
r=mid-1,ans=mid;
else
l=mid+1;
}
if(ans>tot)
{
fl=0;
return;
}
else
f[u]=a[ans];
}
}
bool ok(int w)
{
fl=1;
dfs(1,0,w);
return fl;
}
int main()
{
n=read();
for(int i=1;i<n;i++)
{
int x=read(),y=read();
add(x,y),add(y,x);
}
for(int i=1;i<=n;i++)
ans+=(d[i]-1)>>1;
int l=1,r=n,len=1;
while(l<=r)
{
int mid=(l+r)>>1;
if(ok(mid))
r=mid-1,len=mid;
else
l=mid+1;
}
printf("%d %d\n",ans,len);
return 0;
}