1. 程式人生 > >[2018.10.24 T3] 老大

[2018.10.24 T3] 老大

暫無連結

老大

【題目描述】

因為 OB 今年拿下 4 4 塊金牌,學校贊助擴建勞模辦公室為勞模辦公室群,為了體現 OI 的特色,辦公室群被設計成了樹形 n n 個點 n

1 n−1 條邊的無向連通圖),由於新建的辦公室太大以至於要將獎盃要分放在兩個不同的地方以便同學們丟硬幣進去開光,OB 想請你幫幫他看看獎盃放在哪兩個辦公室使得在任意一個在勞模辦公室做題的小朋友能最快地找到獎盃來開光。

一句話題意:給出一個 n n

個點的樹,在兩個合適且不同的點放上獎盃,使得每個點到最近的獎盃距離最大值最小。

【輸入】

第一行,一個整數 n n

接下來的 n 1

n−1 行,每行兩個數 x , y x,y

【輸出】

一個數,表示最小的最大距離。

【輸入樣例】

5
1 2
2 3
3 4
4 5

【輸出樣例】

1

【提示】
輸入樣例#2

8
1 2
1 3
2 4
2 5
3 6
3 7
1 8

輸入樣例#2

2

【資料規模與約定】

對於前 60 % 60\% 的資料, n 100 n≤100

對於前 80 % 80\% 的資料, n 2000 n≤2000

對於 80 % 80\% 的資料,保證樹的形態隨機。

對於 100 % 100\% 的資料,保證 3 n 200000 3≤n≤200000

題解

二分最大距離,用將軍令 c h e c k check 看是否能用2個點覆蓋,複雜度 O ( n log n ) O(n\log n)

程式碼
#include<bits/stdc++.h>
using namespace std;
const int M=2e5+5;
int head[M],nxt[M<<1],to[M<<1],dis[M],l,r,mid,cnt,n,ans;
void add(int f,int t){nxt[++cnt]=head[f],head[f]=cnt,to[cnt]=t;}
void dfs(int f,int v,int k)
{
	int mx=0,mn=M;
	for(int i=head[v];i;i=nxt[i])if(to[i]!=f)dfs(v,to[i],k),mx=max(mx,dis[to[i]]+1),mn=min(mn,dis[to[i]]+1);
	dis[v]=(mx+mn<0?mn:mx);
	if(dis[v]>=k)dis[v]=-k-1,++ans;
}
bool check(int k){dfs(ans=0,1,k);if(dis[1]>=0)++ans;return ans>2;}
void in(){scanf("%d",&n);for(int i=1,a,b;i<n;++i)scanf("%d%d",&a,&b),add(a,b),add(b,a);}
void ac(){for(l=0,r=n;l^r;)mid=l+r>>1,check(mid)?l=mid+1:r=mid;printf("%d",l);}
int main(){in(),ac();}