給你一棵樹,你可以選擇斷開一條邊再連上一條邊使樹的直徑最小,求最小直徑,可以斷的邊,任意一組可以斷的點和新連的點。
算是求樹上直徑的綜合運用。
先考慮乙個問題:現在有兩棵樹,直徑分別為$l_1$,$l_2$,
那麼將兩棵樹合併後新樹的直徑最小為$\max(l_1,l_2,\lceil\frac\rceil+\lceil\frac\rceil+1)$。
顯然合併時的最優選擇為連上兩棵樹的中點,因為任何其他位置都不會使新樹的直徑更短。
本題中我們先dfs預處理出直徑的兩個端點,標記出一條直徑。
然後分別以兩個端點為根樹形dp出每個子樹內的最長鏈。
列舉斷邊,若斷邊在當前直徑上,那麼我們可以用預處理出的dp值求出新樹的直徑。
若不在直徑上,那麼一棵子樹的直徑為原直徑,另乙個子樹的直徑可用dp表示。
記錄一下最小值以及取最小值的樹的編號即可。
至於第三問,我們隨便找到一條符合的邊,找到切斷它後兩棵子樹的直徑的中點連邊即可。
#include#include#include#includeusing namespace std;#define r register
#define ll long long
inline ll read()
return aa;
}inline int max(int x,int y)
const int n=300003,m=600003;
struct edgeed[n];
int tot,first[n],ver[m],nxt[m];
inline void add(int x,int y)
int n,f[n],fg[n],g[m],ve[n],ansi;
void dp(int x,int fa,int ei)
fg[x]=max(fg[x],g[ei]);
}int dis[n],pre[n],vi[n],pi,pj;
void dfsi(int x,int fa)
}int dfsj(int x,int fa,int dep)
return 0;
}void dfsk(int x,int fa,int lim)
}void getline()
void solve(int x,int y)
int main(); }
dis[1]=0;pj=1;dfsi(1,1);pi=pj;
dis[pi]=0;dfsi(pi,pi);ansi=dis[pj];
getline();//zhao dao zhi jing
dp(pj,pj,1);dp(pi,pi,1);
for(r int i=1,x,y,u,v,z;iprintf("%d\n%d ",ansi,ve[0]);
for(r int i=1;i<=ve[0];++i)printf("%d ",ve[i]);puts("");
solve(ed[ve[1]].u,ed[ve[1]].v);return 0;}/*
41 2
2 3
3 4
*/
SDOI2013 直徑(樹的直徑)
小q最近學習了一些圖論知識。根據課本,有如下定義。樹 無迴路且連通的無向圖,每條邊都有正整數的權值來表示其長度。如果一棵樹有n個節點,可以證明其有且僅有n 1 條邊。路徑 一棵樹上,任意兩個節點之間最多有一條簡單路徑。我們用 dis a,b 表示點a和點b的路徑上各邊長度之和。稱dis a,b 為a...
樹的直徑 板子
不帶解釋版模板o n 常數較大,但是可以知道樹上每一點到直徑端點的距離,這個大多時候都很有用 struct node e maxn 2 ll dis1 maxn dis2 maxn int st,ed,max len void dd int u,int fa,int len,int flag fil...
樹的直徑相關
一.樹直徑的定義.顯然一棵樹可以有不止一條直徑.二.樹直徑的求解.一般來說樹的直徑可以用樹形dp來求.設f i 0 1 f i 0 1 f i 0 1 表示在i ii的子樹中以i ii為一端的最長 次長鏈長度,我們可以很容易dp出來這個值,樹的直徑就是f i 0 f i 1 f i 0 f i 1 ...