題目描述?Description
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 Description
* 第1行: 兩個空格分隔的整數V和S
* 第2...V行: 兩個空格分隔的整數A_i和B_i
輸出描述?Output Description
* 第1行:一個整數,表示FJ可以獲得的最大的直徑。
樣例輸入?Sample Input
7 2
6 7
3 4
6 5
1 2
3 2
4 5
樣例輸出?Sample Output
2
數據范圍及提示?Data Size & Hint
對于50%的數據,滿足V<=100;
對于100%的數據,滿足V<=100000
/*二分答案+貪心記錄以每個節點為根的子樹的每條鏈的長度,如果最長鏈和次長鏈的和大于二分出的答案,就減去最長鏈。 */ #include<cstdio> #include<iostream> #include<algorithm> #include<cstring> #define N 100010 using namespace std; int head[N],f[N],g[N],n,m,sum; struct node {int v,pre; };node e[N*2]; void add(int i,int x,int y) {e[i].v=y;e[i].pre=head[x];head[x]=i; } bool cmp(int s1,int s2) {return s1>s2; } void dfs(int x,int fa,int mid) {for(int i=head[x];i;i=e[i].pre)if(e[i].v!=fa)dfs(e[i].v,x,mid);int cnt=0;for(int i=head[x];i;i=e[i].pre)if(e[i].v!=fa)g[++cnt]=f[e[i].v]+1;sort(g+1,g+cnt+1,cmp);if(cnt==0){f[x]=0;return;}else if(cnt==1){if(g[1]<=mid)f[x]=g[1];else sum++,f[x]=0;return;}else{int i=2;for(;i<=cnt;i++)if(g[i-1]+g[i]<=mid)break;else sum++;if(i==cnt+1&&g[cnt]>mid)sum++;f[x]=g[i-1];return;} } bool check(int mid) {memset(f,0,sizeof(f));memset(g,0,sizeof(g));sum=0;dfs(1,0,mid);if(sum>m)return false;return true; } int main() {scanf("%d%d",&n,&m);for(int i=1;i<n;i++){int x,y;scanf("%d%d",&x,&y);add(i*2-1,x,y);add(i*2,y,x);}int l=0,r=n-1,ans=n-1;while(l<=r){int mid=(l+r)>>1;if(check(mid)){r=mid-1;ans=mid;}else l=mid+1;}printf("%d",ans);return 0; }
?