作为一个电子商务为主体的公司,京东一直努力实现自己“多、快、好、省”的承诺。其中,“快”的特质更是被京东发挥到了极致。京东建立了一个非常高效的物流网络,物流网络构成了一个树结构,由很多的物流点和将物流点连结起来的道路组成。
京东物流网络中每个物流点有一个权值 di,物流点间的道路也都有一个权值 wi。对于一条物流网络中的路径,令路径上所有物流点权值 di 的最小值为 mind,路径上所有道路权值 wi 的总和为 sumw,则该条路径的总权值为 mind* sumw。路径的起点和终点可以是物流网络中的任意物流点,且路径中不能出现重复的物流点。
请求出京东的这个树形物流网络所有路径总权值中的最大值。
输入格式
第一行输入一个整数 T(1 ≤ T ≤ 50),表示数据组数。
每组数据第一行一个整数 n(1 ≤ n ≤ 105),表示有 n 个物流点。
之后一行 n 个整数,表示每个物流点的权重 di(1 ≤ di ≤ 109)。
接下来有 n - 1 行,每行 3 个整数 ui,vi,wi(1 ≤ ui, vi ≤ n, 1 ≤ wi ≤ 109),表示有一条连接 ui 和 vi 的权值为 wi的道路。输入数据保证没有重复出现的(ui,vi)点对,且最终一定会形成一个树形物流网络。
最多有 10 组数据的 n 超过 104。
输出格式
一共输出 T 行,每行一个整数,表示路径总权值的最大值。
样例1
输入:
1
3
1 2 3
1 2 1
1 3 2
输出:
3
提示信息
总权值最大的路径是 2 - 1 - 3,mind 为 min(1, 2, 3) = 1,sumw 为 sum(1, 2) = 3,因此总权值为 1 * 3 = 3。
思路:树分治。求得重心,以重心为树根遍历所在子树,求得所有点到根的路径信息(子树编号、路径长度、路径最小点权)。
关键在于如何高效率的更新答案,思路为先按照路径最小点权由小到大排序,从后往前遍历,对于当前路径,满足要求的解为,树编号不一样的且不小于其最小点权的路径长度最大的。所以要记录并更新之前的最长路径,和与最长路径子树编号不一样的次长路径,这样最优答案即为其中与当前路径编号不一样的一个。
当时比赛时不知道树分治的思想,普通思路一直超时。题解说点分治,并不知道什么意思,这几天学习了树分治想起这个题,便根据自己的理解敲了一遍。
#pragma comment(linker, "/STACK:10240000000,10240000000")
#include<iostream>
#include<stdio.h>
#include<math.h>
#include <string>
#include<string.h>
#include<map>
#include<queue>
#include<set>
#include<utility>
#include<vector>
#include<algorithm>
#include<stdlib.h>
using namespace std;
#define eps 1e-8
#define pii pair<int,int>
#define inf 0x3f3f3f3f
#define rd(x) scanf("%d",&x)
#define rd2(x,y) scanf("%d%d",&x,&y)
#define rd3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define mo(x) memset(x,0,sizeof(x))
#define ll long long int
#define mod 1000000007
#define maxn 101000
#define maxm 10000001
int mi(int a,int b){return a<b?a:b;}
int ma(int a,int b){return a>b?a:b;}
struct node
{
int v,w,next;
}edge[maxn*2];
int head[maxn],vis[maxn],tot;
int d[maxn],sz[maxn],mx[maxn];
//ll dis[maxn];
ll res;
int n,nn;
void init(){
tot=0;
memset(head,-1,sizeof head);
mo(vis);
}
void addedge(int u,int v,int w){
edge[tot].v=v;edge[tot].w=w;edge[tot].next=head[u];
head[u]=tot++;
}
void dfsize(int x,int fa){//统计子树大小
sz[x]=1;mx[x]=0;
for(int i=head[x];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(v==fa||vis[v]) continue;
dfsize(v,x);
sz[x]+=sz[v];
if(sz[v]>mx[x]) mx[x]=sz[v];
}
}
void dfsroot(int r,int x,int fa,int &root,int &mm){//求重心
if(sz[r]-sz[x]>mx[x]) mx[x]=sz[r]-sz[x];
if(mx[x]<mm) mm=mx[x],root=x;
for(int i=head[x];i!=-1;i=edge[i].next){
int v=edge[i].v;
if(v==fa||vis[v]) continue;
dfsroot(r,v,x,root,mm);
}
}
struct dd//保存到中心的路径的sumw、k(分支编号)、md(最小点权)
{
ll w;
int md,k;
dd(){}
dd(int kk,int mmd,ll ww){w=ww;md=mmd;k=kk;}
}dis[maxn];
bool cmp(dd a,dd b){
return a.md<b.md;
}
void dfsdis(int r,int x,int fa,int k,ll ww,int md){//求得从中心出发的所有路径
dis[nn++]=dd(k,md,ww);
for(int i=head[x];i!=-1;i=edge[i].next){
int v=edge[i].v;
int w=edge[i].w;
if(v==fa||vis[v]) continue;
dfsdis(r,v,x,k,ww+w,mi(md,d[v]));
}
}
void cal(int r){
nn=0;
int k=1;
for(int i=head[r];i!=-1;i=edge[i].next)
{
int v=edge[i].v;
int w=edge[i].w;
if(vis[v]) continue;
//dis[nn]=0;
dfsdis(r,v,r,k,w,mi(d[v],d[r]));
k++;
}
sort(dis,dis+nn,cmp);
dd m1=dd(0,0,0);
dd m2=dd(0,0,0);
for(int i=nn-1;i>=0;i--){//遍历路径更新答案
ll ww=m1.k==dis[i].k?m2.w:m1.w;
ll res1=(ww+dis[i].w)*dis[i].md;
res=res<res1?res1:res;
if(dis[i].w>=m1.w){
if(dis[i].k==m1.k) m1=dis[i];
else {
m2=m1;m1=dis[i];
}
}
else if(dis[i].w>m2.w&&dis[i].k!=m1.k) m2=dis[i];
}
}
void solve(int x){
dfsize(x,x);
int mm=n+1;
int root;
dfsroot(x,x,x,root,mm);//求重心
cal(root);//计算过重心的最优答案
vis[root]=1;
for(int i=head[x];i!=-1;i=edge[i].next){//分治
int v=edge[i].v;
if(vis[v]) continue;
solve(v);
}
}
int main()
{
int t,u,v,w;
rd(t);
while(t--){
rd(n);
init();
for(int i=1;i<=n;i++) rd(d[i]);
for(int i=1;i<n;i++){
rd3(u,v,w);
addedge(u,v,w);
addedge(v,u,w);
}
res=0;
solve(1);
printf("%lld\n",res);
}
return 0;
}