[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])$是不可行的,因為這不保證合法,但我們想,當我們選出兩條兒子所在的直鏈,發現當他們接在一起時長度過大,需要從中砍斷的時候,會有這樣的事情:
- 砍較長鏈鏈頂的邊最優
- 砍完該邊後,該鏈不再對父節點造成影響
第二點顯然,砍完之後該鏈與父節點不再屬於同一聯通塊內,故不會再影響
第一點也很顯然(廢話),如果我們砍較短鏈,或者不是鏈頂的邊,那麽最長直鏈可能還會與其他直鏈相接再次產生不合法鏈,需要多砍一次,所以砍較長鏈鏈頂的邊是最優的
那麽我們就可以處理了,將當前節點的所有$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
[Usaco2010 Dec]Exercise 奶牛健美操