bzoj2067: [Poi2004]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
7 8
4 5
5 6
1 2
3 2
9 8
2 5
5 8
Sample Output
4 2
HINT
考虑从1开始dfs,每次对于子树两个两个合并,多出来的往上合,第一问答案就是1+Σ(deg[x]-1)>>1
第二问答案显然具有可二分性,现在只需要判断答案x合不合法
对于每个点,把它子树所有点向上需要的答案统计出来到list[]中。
如果子树个数是偶数且这个点不是1号点,则额外加一个a[i]=0(因为有可能将一支向上连会更优,例子在后面)
然后对a排序,二分删掉a中的一个元素,从大到小匹配判断是否合法,如果任何方案都不合法,则是不合法的直接退出
如果弄到最后都合法,这个答案就合法,不过要注意判断根的时候如果子树是偶数个不能额外加元素,也不能二分判断,而要直接判断合法性
例子:
19
3 6
11 14
14 19
12 7
8 5
10 4
3 11
7 18
17 2
6 12
3 10
11 1
11 8
9 16
1 9
16 15
15 13
4 17
图:
code:
1 #include<cstdio> 2 #include<iostream> 3 #include<cmath> 4 #include<cstring> 5 #include<algorithm> 6 #define maxn 10005 7 #define inf 1061109567 8 using namespace std; 9 char ch; 10 int n,a,b,tot,ans,now[maxn],son[maxn<<1],pre[maxn<<1],deg[maxn],cnt,up[maxn],list[maxn]; 11 bool ok; 12 void read(int &x){ 13 for (ok=0,ch=getchar();!isdigit(ch);ch=getchar()) if (ch=='-') ok=1; 14 for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar()); 15 if (ok) x=-x; 16 } 17 void put(int a,int b){pre[++tot]=now[a],now[a]=tot,son[tot]=b,deg[a]++;} 18 bool check(int x,int lim){ 19 for (int i=1,j=cnt;i<j;i++,j--){ 20 if (i==x) i++; if (j==x) j--; 21 if (list[i]+list[j]>lim) return false; 22 } 23 return true; 24 } 25 bool dfs(int u,int fa,int lim){ 26 for (int p=now[u],v=son[p];p;p=pre[p],v=son[p]) 27 if (v!=fa) if (!dfs(v,u,lim)) return false; 28 cnt=0; 29 for (int p=now[u],v=son[p];p;p=pre[p],v=son[p]) 30 if (v!=fa) list[++cnt]=up[v]+1; 31 if (!cnt) return true; 32 if (u!=1&&!(cnt&1)) list[++cnt]=0; 33 sort(list+1,list+cnt+1); 34 if (u==1&&!(cnt&1)) return check(0,lim); 35 int l=1,r=cnt,m; 36 while (l!=r){ 37 m=(l+r)>>1; 38 if (check(m,lim)) r=m; else l=m+1; 39 } 40 if (check(l,lim)) up[u]=list[l]; 41 else up[u]=inf; 42 return up[u]<=lim; 43 } 44 int calc(){ 45 int l=1,r=n-1,m; 46 while (l<r){ 47 m=(l+r)>>1; 48 if (dfs(1,0,m)) r=m; 49 else l=m+1; 50 } 51 return l; 52 } 53 int main(){ 54 while (~scanf("%d",&n)){ 55 tot=0; 56 memset(now,0,sizeof(now)); 57 memset(deg,0,sizeof(deg)); 58 memset(up,0,sizeof(up)); 59 for (int i=1;i<n;i++) read(a),read(b),put(a,b),put(b,a); 60 ans=1; 61 for (int i=1;i<=n;i++) ans+=(deg[i]-1)>>1; 62 printf("%d %d\n",ans,calc()); 63 } 64 return 0; 65 }