[题意]
一棵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>