计蒜客444-京东的物流路径(树上分治算法)

作为一个电子商务为主体的公司,京东一直努力实现自己“多、快、好、省”的承诺。其中,“快”的特质更是被京东发挥到了极致。京东建立了一个非常高效的物流网络,物流网络构成了一个树结构,由很多的物流点和将物流点连结起来的道路组成。

京东物流网络中每个物流点有一个权值 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。

分治算法:分三步进行

第一步:深度优先遍历整棵树,对于子树选择子树的重心作为子树的root节点继续递归

第二步:深度优先遍历每个子树,考虑所有的分支节点到root节点之间的路径长度sumw和路径最小点权mind和相对于root的子树分支编号id,用stk[]数组保存三个变量。

第三步:将stk[]数组中按mind从大到小排序,依次考虑每一条终点为root节点的路径,合并id编号不同的两条路径(root即为中间节点),保留考虑过的路径中路径长度最长的路径path1,和与path1路径不同id号的次长路径path2(这样不至于两个最长路径(id号相同)合并时出现路径重合情况,解决方案是和path2合并路径)

注:第三步做法的依据是先从mind大号枚举路径考虑到mind较小节点时,我们最想和此路径(mind较小点所在路径)和并的路径是之前考虑过的路径中路径最长的路径。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define N 210000
#define LL long long
#define inf 0x0f0f0f0f
struct Node{
    int nxt,val;
    Node(){}
    Node(int _n,int _v){ nxt = _n; val = _v; }
};
struct STK{
    int id,wei;
    LL len;
    STK(){}
    STK(int _id,int _w,LL _l){
        id = _id; wei = _w; len = _l;
    }
};
STK stk[N];
vector<Node> nod[N];
int size[N],vis[N],f[N],wgt[N];
int root,top;
LL ans;
LL max(LL a,LL b){ return a>b?a:b; }
LL min(LL a,LL b){ return a<b?a:b; }
int Cmp(STK k1,STK k2){ return k1.wei > k2.wei; }
void getRoot(int cur,int fa,int sum){
    int tmp = 0;
    size[cur] = 1;
    for(int i = 0;i<(int)nod[cur].size();i++){
        int nxt = nod[cur][i].nxt;
        if(vis[nxt]||nxt==fa) continue;
        getRoot(nxt,cur,sum);
        size[cur] += size[nxt];
        tmp = max(tmp,size[nxt]);
    }
    f[cur] = max(tmp,sum - size[cur]);
    if(f[root]>f[cur]) root = cur;
}
void dfs(int cur,int fa,LL len,int wei,int id){
    stk[++top] = STK(id,wei,len);
    for(int i = 0;i<(int)nod[cur].size();i++){
        int nxt = nod[cur][i].nxt;
        int val = nod[cur][i].val;
        if(vis[nxt]||nxt==fa) continue;
        dfs(nxt,cur,len+val,min(wei,wgt[nxt]),id);
    }
}
void CDQ(int cur,int sum){
    root = 0; f[root] = inf;
    getRoot(cur,-1,sum);
    int tmp_r = root;
    vis[tmp_r] = 1;
    //printf("====%d,%d\n",cur,tmp_r);
    for(int i = 0;i<(int)nod[tmp_r].size();i++){
        int nxt = nod[tmp_r][i].nxt;
        if(vis[nxt]) continue;
        CDQ(nxt,size[nxt]);
    }
    int id = 0;
    top = 0;
    for(int i = 0;i<(int)nod[tmp_r].size();i++){
        int nxt = nod[tmp_r][i].nxt;
        if(vis[nxt]) continue;
        int val = nod[tmp_r][i].val;
        dfs(nxt,tmp_r,val,min(wgt[nxt],wgt[tmp_r]),++id);
    }
    sort(stk+1,stk+top+1,Cmp);
    STK pri1 = STK(0,inf,0),pri2 = STK(-1,inf,0);
    for(int i = 1;i<=top;i++){
        if(pri1.id!=stk[i].id)
            ans = max(ans,(LL)stk[i].wei*(stk[i].len + pri1.len));
        else
            ans = max(ans,(LL)stk[i].wei*(stk[i].len + pri2.len));
        if(pri1.len < stk[i].len){
            if(pri1.id==stk[i].id)
                pri1 = stk[i];
            else{
                pri2 = pri1;
                pri1 = stk[i];
            }
        }
        else if((pri2.len<stk[i].len)&&(stk[i].id!=pri1.id)){
            pri2 = stk[i];//没有考虑到WA了n多发
        }
    }
    vis[tmp_r] = 0;
}
void init(){
    for(int i = 0;i<N;i++) nod[i].clear();
    memset(vis,0,sizeof(vis));
}
int main(){
    int T,a,b,n,v;
    //freopen("Test.txt","r",stdin);
    scanf("%d",&T);
    while(T--){
        init();
        scanf("%d",&n);
        for(int i = 1;i<=n;i++)
            scanf("%d",&wgt[i]);
        for(int i = 1;i<n;i++){
            scanf("%d%d%d",&a,&b,&v);
            nod[a].push_back(Node(b,v));
            nod[b].push_back(Node(a,v));
        }
        ans = 0;
        CDQ(1,n);
        printf("%lld\n",ans);
    }
    return 0;
}




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值