題意:
有一棵樹,樹上有許多人,他們要聚會,找乙個點使得所有人到這個點的距離的最大值最小。
題解:首先,以乙個有人的點為根,求乙個生成樹,刪掉所有沒有人的子樹,保證所有的懸掛點(只連線一條邊的點)都是有人的節點,以保證後面求出的直徑的兩端是兩個有人節點。為什麼非得以有人的節點為根呢?因為如果找了乙個沒人的節點當根,而這個根又恰好是乙個懸掛點,那麼可以想象,這個懸掛點可能成為直徑的端點。
其次,求樹的直徑,就是樹上兩點的最長距離,這個直徑的兩個端點必然都是懸掛點。
直徑/2向上取整就是半徑,也就是題目所求距離。
求直徑可跑兩遍dfs,第一遍從任意點開始,找出與此點距離最大點,第二遍從這個點開始,找出與這個點距離最大點。
賽時想的是樹的重心,wa了,想像一棵樹上半部分是一條長鏈,下半部分是密密麻麻的兒子接在長鏈一端。然後又想了拓撲求直徑,t了。
但是發現拓撲排序的輪數其實就是樹的半徑,不知道這個結論有什麼用。
#include#include#define inf 0x3f3f3f3f#include
#include
#include
using
namespace
std;
vector
lnk[100005
];vector
sclnk[100005];//
生成樹intn;
int scn;//
生成樹的節點數
bool ren[100005
];bool dfs(int fa,int
u) ok=ok||tr;
}return
ok;}
intkg;
void prt(int fa,int
u) kg--;}
bool vis[100005
];int size[100005];
int dis[100005
];struct
p p(
int n1,int
dis1)
friend
bool
operator >(const p &a,const p &b)
friend
bool
operator
<(const p &a,const p &b)};
void ddfs(int
st)
}} intmain()
intr1;
for(int i=1;i<=k;i++)
dfs(-1
,r1);
//prt(r1);
//求生成樹
memset(dis,inf,
sizeof
dis);
dis[r1] = 0
; ddfs(r1);
//求樹的直徑
int maxi=r1;
for(int i=1;i<=n;i++)
memset(dis,inf,
sizeof
dis);
memset(vis,
0,sizeof
vis);
dis[maxi] = 0
; ddfs(maxi);
int maxx=0
;
for(int i=1;i<=n;i++)
printf(
"%d\n
",(maxx+1)/2
);
return0;
}
2019牛客多校第四場 A meeting
考場上寫了一大坨樹形dp,寫的時候就感覺我這不是跟求樹的最長鏈寫的一毛一樣 然後考後看題解,果然是k個ren所連成的子樹的最長鏈的一半 可以利用反證法證明,如果在長度為d的最長鏈的中間放乙個中心,如果有另外乙個點到這個點的長度 d 1 2,那麼這個點到對面的那個點的長度大於d,所以不存在這樣乙個點。...
2019牛客多校第四場A meeting 思維
乙個樹上有若干點上有人,找出乙個集合點,使得所有人都到達這個點的時間最短 無碰撞 就是找樹的直徑,找直徑的時候記得要找有人的點 include include includeusing namespace std define pb push back define f first define s...
free(牛客多校第四場)
free 本題可以通過常規的最短路演算法改變來得到,通過在最短路演算法中加乙個長度為k的迴圈,判斷k條路免費後的最小權值即可。include include include include include include include include include define inf 0x3f...