[題意]
一棵n個節點的樹,求兩條路徑s1,s2最大的長度積,滿足s1,s2都是兩點之間的最短路徑,而且兩條路徑不含任何公共點.(n<=3000).
[思路]
O(n^2):
枚舉樹上的每條邊,把此邊從樹上移除,那麼原來的樹就變成了兩棵子樹:所求的兩條路徑分別在兩棵子樹中.那麼爲了滿足題意中的最大長度積,分別求出兩棵子樹的直徑即可.
O(n):
通過模擬(應該有別的方法,反正我是通過模擬來猜測的)可以假想:兩條路徑一定有一條的端點是直徑的某一個端點.
所有答案分爲以下兩種情況:
1.兩條路徑的某個端點分別是直徑的兩個端點.
2.其中一條路徑就是直徑.
明確了兩種情況就可以枚舉+貪心,預處理出直徑上每個點內最深的深度和不經過直徑上的任意點的最長路徑就可以完成O(n).
<span style="font-size:18px;">#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
inline void rd(int &res){
res=0;char c;
while(c=getchar(),c<48);
do{
res=(res<<1)+(res<<3)+(c^48);
}while(c=getchar(),c>=48);
}
const int M=3005;
int fa[M],c=0,n,id[M],dm[M],ID1,ID2,mx=0,tot=0,val[M],tmp[M];
vector<int>e[M];
void dfs(int x,int f,int d){
if(c)fa[x]=f;
if(d>=mx){
mx=d;ID2=x;
}
for(int i=0;i<e[x].size();i++){
int y=e[x][i];
if(y==f||id[y])continue;
dfs(y,x,d+1);
}
}
void idfs(int x,int f,int d){
val[id[x]]=max(val[id[x]],d);
for(int i=0;i<e[x].size();i++){
int y=e[x][i];
if(y==f||id[y])continue;
id[y]=id[x];
idfs(y,x,d+1);
}
}
int solve(int x){
int res=0;
for(int i=0;i<e[x].size();i++){
int y=e[x][i];
if(y==fa[x]||id[y])continue;
mx=0;
dfs(y,x,0);
dfs(ID2,ID2,0);
res=max(mx,res);
}
return res;
}
int main(){
int i,j,k,a,b,ans=0,mxd=0;
scanf("%d",&n);
for(i=1;i<n;i++){
rd(a);rd(b);
e[a].push_back(b);
e[b].push_back(a);
}
dfs(1,0,0);
ID1=ID2;c=1;
dfs(ID1,0,0);c=0;
int x=ID2;
while(x!=0){
id[x]=++tot;
dm[tot]=solve(x);
mxd=max(mxd,dm[tot]);//不經過直徑的最長路徑
idfs(x,fa[x],0);
x=fa[x];//遍歷直徑.
}
for(i=tot;i>=1;i--)tmp[i]=max(tmp[i+1],tot-i+val[i]);//val[i]表示直徑上編號爲i的點下的最大深度.
for(i=1;i<=tot;i++)ans=max(ans,(val[i]+i-1)*tmp[i+1]);
ans=max(ans,(tot-1)*mxd);
printf("%d\n",ans);
return 0;
}
/**************************************************************
Problem: 3006
User: LIN452
Language: C++
Result: 正確
Time:0 ms
Memory:1856 kb
****************************************************************/</span>