天天看點

計算客444 (2015計蒜之道 京東的物流路徑)

題目連結

作為一個電子商務為主體的公司,京東一直努力實作自己“多、快、好、省”的承諾。其中,“快”的特質更是被京東發揮到了極緻。京東建立了一個非常高效的物流網絡,物流網絡構成了一個樹結構,由很多的物流點和将物流點連結起來的道路組成。

京東物流網絡中每個物流點有一個權值 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;
}