題目大意
- 給出一棵帶邊權的樹;
- 問1:求出樹的直徑;
- 問2:有多少對點的距離等於樹的直徑。
題目分析
- 直徑的定義: 樹上最長的鏈(可能有多條)。
- 求樹的直徑的方法1:用兩次dfs來完成
dfs1從根出發,找到最遠的葉子結點 k;
dfs2以 k 爲根,出發,找到離他最遠的點 t , k 與 t 之間的距離就是直徑。
- 求直徑的做法2:dp的思維來實現
從根出發,搜索的過程中,同時記錄最長鏈與次長鏈,回溯的時候更新。
- 本題解抄的就是方法2,而且更巧妙的是,用一個緩存 tmp 來記錄可能最長鏈,節省了一丁點的空間。
解題流程
- 從根,開始搜索:
- 回溯到 x 的時候 5 個操作:
- 1 用 tmp 記錄經過 y 的最長鏈長度:
tmp=len[y]+e(x,y); 其中 e(x,y) 是(x,y) 之間的邊長;
- 2 如果 ans<tmp+len[x] 則更新 鏈長 ans 和 點對 sum:
ans=tmp+len[x];
sum=nod[x]∗nod[y];
- 3 如果 ans==tmp+len[x] 則更新 點對 sum:
sum+=nod[x]∗nod[y];
- 4 如果 tmp>len[x] 則更新 x 的參數: len[x] 和 nod[x] :
len[x]=tmp;
nod[x]=nod[y];
- 5 如果 tmp==len[x] 則更新 x 的參數: nod[x]:
nod[x]+=nod[y];
- 最後答案是: ans 和 sum 。
參考代碼
#include<bits/stdc++.h>
using namespace std;
const int N=500007;
const int inf=1e9+7;
struct node{
int nex,to,c;
}e[N];
int n,ans,sum,len[N],nod[N];
int las[N],cnt;
void add(int x,int y,int c){
cnt++;
e[cnt].nex=las[x];
e[cnt].to=y;
e[cnt].c=c;
las[x]=cnt;
}
void dfs(int x,int fa){
len[x]=0;
nod[x]=1;
for(int i=las[x];i;i=e[i].nex){
int y=e[i].to;
if(y==fa) continue;
dfs(y,x);
int tmp=e[i].c+len[y];
if(tmp+len[x]>ans) ans=tmp+len[x],sum=nod[x]*nod[y];
else if(tmp+len[x]==ans) sum+=nod[x]*nod[y];
if(tmp>len[x]) len[x]=tmp,nod[x]=nod[y];
else if(tmp==len[x]) nod[x]+=nod[y];
}
}
int main(){
while(~scanf("%d",&n)){
memset(las,0,sizeof(las));
int x,y,c;
cnt=0;
for(int i=1;i<n;i++){
scanf("%d %d %d",&x,&y,&c);
add(x,y,c); add(y,x,c);
}
ans=-inf;
sum=0;
dfs(1,0);
printf("%d %d\n",ans,sum);
}
return 0;
}