真正的危机不是机器人像人一样思考,而是人像机器一样思考。 ——凉宫春日的忧郁

[Usaco2010 Dec]Exercise 奶牛健美操

[Usaco2010 Dec]Exercise 奶牛健美操

题目

Farmer John为了保持奶牛们的健康,让可怜的奶牛们不停在牧场之间 的小路上奔跑。这些奶牛的路径集合可以被表示成一个点集和一些连接 两个顶点的双向路,使得每对点之间恰好有一条简单路径。简单的说来, 这些点的布局就是一棵树,且每条边等长,都为1。 对于给定的一个奶牛路径集合,精明的奶牛们会计算出任意点对路径的最大值, 我们称之为这个路径集合的直径。如果直径太大,奶牛们就会拒绝锻炼。 Farmer John把每个点标记为1..V (2 <= V <= 100,000)。为了获得更加短 的直径,他可以选择封锁一些已经存在的道路,这样就可以得到更多的路径集合, 从而减小一些路径集合的直径。 我们从一棵树开始,FJ可以选择封锁S (1 <= S <= V-1)条双向路,从而获得 S+1个路径集合。你要做的是计算出最佳的封锁方案,使得他得到的所有路径集合 直径的最大值尽可能小。 Farmer John告诉你所有V-1条双向道路,每条表述为:顶点A_i (1 <= A_i <= V) 和 B_i (1 <= B_i <= V; A_i!= B_i)连接。 我们来看看如下的例子:线性的路径集合(7个顶点的树) 1---2---3---4---5---6---7 如果FJ可以封锁两条道路,他可能的选择如下: 1---2 | 3---4 | 5---6---7 这样最长的直径是2,即是最优答案(当然不是唯一的)。

INPUT

第1行: 两个空格分隔的整数V和S * 第2...V行: 两个空格分隔的整数A_i和B_i

OUTPUT

第1行:一个整数,表示FJ可以获得的最大的直径。

SAMPLE

INPUT

7 2

6 7

3 4

6 5

1 2

3 2

4 5

OUTPUT

2

解题报告

二分答案$+$贪心验证,竟然放在$DP$专题里= =(可能是我造化不够)

首先我们可以看到题面中闪闪发光的一句话

最大值尽可能小

这东西一眼看上去就知道,二分差不多就是可行的了

我们可以二分该最小值,然后验证其是否合法

我们设$f[i]$表示以第$i$个节点为根的子树中,合法的最长直链的长度

合法:

即保证最长链长度不可大于二分的答案

直链:

指链两端点路径不跨过根节点的链

然后我们就可以用$f[i]$计算要砍去多少条边,从而判断当前二分出的答案的合法性

那么问题来了,如何计算$f[i]$

显然直接求$max(f[son])$是不可行的,因为这不保证合法,但我们想,当我们选出两条儿子所在的直链,发现当他们接在一起时长度过大,需要从中砍断的时候,会有这样的事情:

  1. 砍较长链链顶的边最优
  2. 砍完该边后,该链不再对父节点造成影响

第二点显然,砍完之后该链与父节点不再属于同一联通块内,故不会再影响

第一点也很显然(废话),如果我们砍较短链,或者不是链顶的边,那么最长直链可能还会与其他直链相接再次产生不合法链,需要多砍一次,所以砍较长链链顶的边是最优的

那么我们就可以处理了,将当前节点的所有$f[son]$从大到小排序,依次枚举,判断相邻的两条直链长度相接是否合法,假如合法,将$f[i]$赋值,否则继续枚举,并且记录砍掉的边的数目$++$

注意处理某些奇怪的边界问题

 1 #include<algorithm>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<cstdio>
 5 using namespace std;
 6 inline int read(){
 7     int sum(0);
 8     char ch(getchar());
 9     for(;ch<'0'||ch>'9';ch=getchar());
10     for(;ch>='0'&&ch<='9';sum=sum*10+(ch^48),ch=getchar());
11     return sum;
12 }
13 struct edge{
14     int e;
15     edge *n;
16 }a[200005],*pre[100005];
17 int tot;
18 inline void insert(int s,int e){
19     a[++tot].e=e;
20     a[tot].n=pre[s];
21     pre[s]=&a[tot];
22 }
23 int n,m;
24 int f[100005],fa[100005];
25 int ans;
26 int mid,num;
27 int tmp[100005];
28 inline bool cmp(int x,int y){
29     return x>y;
30 }
31 inline void dfs(int u){
32     bool flag(false);
33     for(edge *i=pre[u];i;i=i->n){
34         int e(i->e);
35         if(e!=fa[u]){
36             flag=true;
37             fa[e]=u;
38             dfs(e);
39         }
40     }
41     if(!flag)
42         return;
43     tmp[0]=0;
44     f[u]=0;
45     for(edge *i=pre[u];i;i=i->n){
46         int e(i->e);
47         if(e!=fa[u])
48             tmp[++tmp[0]]=f[e]+1;
49     }
50     int size(tmp[0]);
51     sort(tmp+1,tmp+1+size,cmp);
52     tmp[size+1]=0;
53     for(int i=1;i<=size;++i){
54         if(tmp[i]+tmp[i+1]>mid)
55             ++num;
56         else{
57             f[u]=tmp[i];
58             break;
59         }
60     }
61 }
62 inline bool check(){
63     num=0;
64     memset(f,0,sizeof(f));
65     dfs(1);
66     if(num<=m)
67         return true;
68     return false;
69 }
70 inline void ef(int l,int r){
71     while(l<=r){
72         mid=(l+r)>>1;
73         if(check())
74             r=mid-1,ans=mid;
75         else
76             l=mid+1;
77     }
78 }
79 int main(){
80     memset(pre,NULL,sizeof(pre));
81     n=read(),m=read();
82     for(int i=1;i<n;++i){
83         int x(read()),y(read());
84         insert(x,y),insert(y,x);
85     }
86     ef(1,n);
87     printf("%d",ans);
88 }
View Code

 

posted @ 2017-09-12 21:35  Hzoi_Mafia  阅读(471)  评论(0编辑  收藏  举报
我们都在命运之湖上荡舟划桨,波浪起伏着而我们无法逃脱孤航。但是假使我们迷失了方向,波浪将指引我们穿越另一天的曙光。 ——死神