loj10155:數字轉換:樹的直徑問題

題目連接

  • 樹形DP專題

題目大意

  • 一個數字n,求n以內的數字之間 “約數和關係” 的最長鏈;
  • 約數和關係:一個數字 xx 的約數和爲 s[x]s[x]

題目分析

  • 約數和關係 可以想到,如果 s[x]<xs[x]<x ,他們之間可以連一條雙向邊;題目就轉換爲了一個樹上,求直徑。
  • 直徑的定義: 樹上最長的鏈。
  • 求樹的直徑的方法1:用兩次dfs來完成
    dfs1dfs1 從根出發,找到最遠的葉子結點 kk
    dfs2dfs2kk 爲根,出發,找到離他最遠的點 ttkktt 之間的距離就是直徑。
  • 求直徑的做法2:dp的思維來實現
    搜索的過程中,同時記錄最長鏈與次長鏈,回溯的時候更新。

解題思路1:兩次 dfsdfs 求直徑

  • 如果 s[x]<xs[x]<x ,他們之間可以連一條雙向邊;
  • dfs1dfs1 從根出發,找到最遠的葉子結點 kk
  • dfs2dfs2kk 爲根,出發,找到離他最遠的點 ttkktt 之間的距離就是直徑。

參考代碼1

//loj10155-數字轉換
//求樹的直徑-兩次dfs求法 
#include<bits/stdc++.h>
using namespace std;
const int N=500007;
struct node{
	int nex,to;
}e[N];
int n,ans,su[N];
int nx,lx;	//第一次的最遠點和最遠距離 
bool b[N];

int las[N],cnt;
void add(int x,int y){
	cnt++;
	e[cnt].nex=las[x];
	e[cnt].to=y;
	las[x]=cnt;
}
void dfs1(int x,int c){   			//找到最遠距離的點 nx 
	if(c>lx){
		lx=c;
		nx=x;
	}
	b[x]=1;
	for(int i=las[x];i;i=e[i].nex){
		int y=e[i].to;
		if(!b[y]) dfs1(y,c+1);
	}
	b[x]=0;
}
void dfs2(int x,int c){				//從nx出發,找反向最遠點 
	ans=max(ans,c);
	b[x]=1;
	for(int i=las[x];i;i=e[i].nex){
		int y=e[i].to;
		if(!b[y]) dfs2(y,c+1);
	}
	b[x]=0;
}
int main(){
	cin >> n;
	for(int i=1;i<=n;i++){			//求約數和 
		for(int j=2;j*i<=n;j++){
			su[j*i]+=i;
		}	
	}
	for(int i=2;i<=n;i++){          //構圖 
		if(i>su[i]){
			add(i,su[i]);
			add(su[i],i);
		}
	}
	dfs1(1,0);
	dfs2(nx,0);
	cout << ans;
	return 0;
} 

解題思路2: dpdp 求直徑

  • 因爲題面要求,對於xx的約數和 s[x]<xs[x]<x ,所以可以設定 s[x]s[x] 爲父親節點, xx 爲子節點。
  • dp的思維來實現
    搜索的過程中,同時記錄最長鏈 d1[x]d1[x] 與次長鏈 d2[x]d2[x],回溯的時候更新。
  • 因爲這是一棵不確定根的樹,所以最後掃描一次所有的點,記錄最大的 d1[x]+d2[x]d1[x]+d2[x] 就是答案。

參考代碼2

//loj10155-數字轉換
//求樹的直徑-dp求法 
#include<bits/stdc++.h>
using namespace std;
const int N=500007;

int n,ans,su[N];
int d1[N],d2[N];

int main(){
	cin >> n;
	for(int i=1;i<=n;i++){			//求約數和 
		for(int j=2;j*i<=n;j++){
			su[j*i]+=i;
		}	
	}
	// s[y] --> y  是 父親到兒子的樹邊 
	for(int y=n;y>=1;y--){
		if(su[y]>=y) continue; 		//約數和必須小於原數 
		int x=su[y];
		if(d1[x]<d1[y]+1){			
			d2[x]=d1[x];			//更新次長邊 
			d1[x]=d1[y]+1;			//更新最大邊 
		} 
		else if(d2[x]<d1[y]+1)
			d2[x]=d1[y]+1;			//更新次長鏈 
	}
	for(int i=1;i<=n;i++) if(d1[i]+d2[i]>ans) ans=d1[i]+d2[i];
	cout << ans;
	return 0;
} 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章