【BZOJ2067】SZN(二分,動態規划,貪心)
題面
Description
String-Toys joint-stock 公司需要你幫他們解決一個問題. 他們想制造一個沒有環的連通圖模型. 每個圖都是由一些頂點和特定數量的邊構成. 每個頂點都可以連向許多的其他頂點.一個圖是連通且無環的. 圖是由許多的線做成的.一條線是一條連接圖中兩個頂點之間的路徑.由於一些技術原因,兩條線之間不能有重疊的部分,要保證圖中任意一條邊都被且僅被一條線所覆蓋.由於一些技術原因,做一個這樣的圖的模型的費用取決於用了多少條線以及最長的那條的長度. (每條邊的長度都為1.),給出對應的圖,求出最少能用多少條線以及在用最少線的情況下最長的那根線最短可以為多少.
Input
第一行僅包含一個數n – 頂點的總數, 2 <= n <= 10 000. 頂點從1 到 n進行編號. 接下來的n - 1 行描述這些邊, 每行兩個數a 和 b, 1 <= a, b <= n, a <> b. 表示頂點a和頂點b之間有一條邊.
Output
輸出兩個數,最少用多少條線以及在用最少線的情況下最長線最短可以為多少.
Sample Input
9
7 8
4 5
5 6
1 2
3 2
9 8
2 5
5 8
Sample Output
4 2
題解
首先第一問答案是\(1+\sum (d_i-1)/2\),其中\(d_i\)是\(i\)的度數。這個東西你可以認為是每個節點的所有兒子兩兩配對,而多出來的部分則可以延伸到父親上面去繼續做。
那么只需要考慮第二問。我們二分一個答案,設\(f[i]\)表示可以向上延伸的最小長度,那么每次對於一個點,把它的所有兒子拿出來排個序,看看延伸上去的最少長度是多少。
當然,這里要分奇偶性來看。如果一個點的兒子數是奇數,那么我們排序之后二分最小的那個延伸上去的兒子。如果是偶數,我們先嘗試兩兩配對,如果不合法那么再考慮一下前面的式子,允許有一個兒子可以延伸到父親去,而偶數的貢獻則只需要匹配兒子數/2-1對,所以可以直接把最大值去掉再當成奇數嘗試匹配。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define MAX 10010
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
struct Line{int v,next;}e[MAX<<1];
int h[MAX],cnt=1,dg[MAX];
inline void Add(int u,int v){e[cnt]=(Line){v,h[u]};h[u]=cnt++;}
int n,ans=1,f[MAX],mid;
int S[MAX],top,vis[MAX];bool fl;
void dfs(int u,int ff)
{
if(!fl)return;
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=ff)dfs(e[i].v,u);
top=f[u]=0;
for(int i=h[u];i;i=e[i].next)
if(e[i].v!=ff)S[++top]=f[e[i].v]+1;
sort(&S[1],&S[top+1]);
if(!top)return;
if(S[top]>mid){fl=false;return;}
if(top%2==0)
{
for(int i=1;i<=top/2;++i)
if(S[i]+S[top-i+1]>mid)
{
if(u!=1){--top;break;}
fl=false;return;
}
}
if(top%2==1)
{
int l=1,r=top,ret=top+1;
while(l<=r)
{
int Mid=(l+r)>>1;bool chk=true;
for(int i=1,j=top;;++i,--j)
{
if(i==Mid)++i;if(j==Mid)--j;
if(i>=j)break;
if(S[i]+S[j]>mid){chk=false;break;}
}
if(chk)ret=Mid,r=Mid-1;
else l=Mid+1;
}
if(ret>top){fl=false;return;}
else f[u]=S[ret];
}
}
int main()
{
n=read();
for(int i=1;i<n;++i)
{
int u=read(),v=read();
Add(u,v);Add(v,u);
dg[u]++;dg[v]++;
}
for(int i=1;i<=n;++i)ans+=(dg[i]-1)/2;
int l=1,r=n,ret=1;
while(l<=r)
{
mid=(l+r)>>1;fl=true;
dfs(1,0);memset(vis,0,sizeof(vis));
if(fl)ret=mid,r=mid-1;
else l=mid+1;
}
printf("%d %d\n",ans,ret);
return 0;
}