题意: 给你一颗n个点的树,树上每个点有一个值,树上每条边长度为1,对于两点x,y之间的代价,x对y的代价可以表示为a[x]*(dis(x,y)),要求任选两点,获得一个最大的价值。
数据范围:n<=50000。
思路: 直接遍历每个点,找距离他最远的点,然后维护最大值 。这个方法只能骗部分分,因为时间复杂度为O(n^2)。
先讲结论: 我们假设这棵树中距离最大的两个点的距离为dismax,这两个点之间的距离称为树的直径,那么对于任何一个点,他在树上距离他最远的点必然是直径的两个端点之一。至于怎么证明,自己思考比较好。我讲的不是很清楚。
找树的直径: 我们先随意以一个点为根,找到离他最远的点,这个点作为端点之一,然后以这个端点为根,找到距离这个根最远的点,这两个点就是直径的端点。
最后我们只要遍历每个点,维护最大值就行了。
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
const int inf=0x7fffffff;
const int mod=1e9+7;
const int eps=1e-6;
typedef long long ll;
typedef unsigned long long ull;
#define ls p<<1
#define rs p<<1|1
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define pii pair<int,int>
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define endl '\n'
#define null NULL
int a[N];
int head[N],nxt[N],to[N],tot=0;
void add(int u,int v)
{
nxt[++tot]=head[u];
to[tot]=v;
head[u]=tot;
}
int de[N],dee[N],deep[N];
void dfs(int x,int y,int *d)
{
d[x]=d[y]+1;
for(int i=head[x];i;i=nxt[i])
{
if(to[i]!=y)
{
dfs(to[i],x,d);
}
}
}
signed main()
{
IOS;
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
for(int i=1;i<=n-1;i++)
{
int u,v;
cin>>u>>v;
add(u,v);add(v,u);
}
dfs(1,0,de);
int ma=0,root1=0;
for(int i=1;i<=n;i++)
{
if(de[i]>ma)
{
ma=de[i];
root1=i;
}
}
dfs(root1,0,dee);
ma=0;
int root2=0;
for(int i=1;i<=n;i++)
{
if(dee[i]>ma)
{
ma=dee[i];
root2=i;
}
}
dfs(root2,0,deep);
int res=0;
for(int i=1;i<=n;i++)
{
res=max(res,max(dee[i]-1,deep[i]-1)*a[i]);
}
cout<<res<<endl;
}