• [题解](树形dp/换根)小x游世界树



    2. 小x游世界树

    (yggdrasi.pas/c/cpp)

    【问题描述】

             小x得到了一个(不可靠的)小道消息,传说中的神岛阿瓦隆在格陵兰海的某处,据说那里埋藏着亚瑟王的宝藏,这引起了小x的好奇,但当他想前往阿瓦隆时发现那里只有圣诞节时才能到达,然而现在已经春天了,不甘心的他将自己的目的地改成了世界树,他耗费了大量的时间,终于将自己传送到了世界树下。世界树是一棵非常巨大的树,它有着许许多多的枝条以及节点,每个节点上都有一个平台。好不容易来到传说中的世界树下,小x当然要爬上去看看风景。小x每经过一条边都会耗费体力值。然而世界树之主想给他弄(gáo)些(dǐan)麻(shì)烦(qíng),

    于是他在每条边上都设了一个魔法阵,当小x踏上那条边时会被传送回根节点,魔法阵只生效一次。这岂不是要累死小x?

    幸运的是,每个平台上都有无数个加速器,这些加速器可以让小x在当前节点所连的边上耗费的体力值减少,不同平台的加速器性能不一定相同,但同一个平台的加速器性能绝对相同。

    世界树之主给了小x一次“换根”的机会,他可以将世界树的任何一个节点变为根,但所有的边都不能改变。小x想问你,将根换为哪个节点能使小x爬到世界树上的每个节点耗费的体力值和最少。默认编号为1的点为初始根。

    【输入】

    第一行一个数n,表示有n个节点。

    第二行n个数ai,表示每个平台上的加速器的性能。

    第三至n+1行,每行三个数bi,ci,di分别表示这条无向边的起点,终点与耗费的能量值

    【输出】

    第一行一个数,表示要换成的节点,如果有多个点为根时耗费的体力值都最小,则输出编号最小的那个。如果保持为1是最优的,就输出1。

    第二行一个数,表示最小耗费的体力值。

    【输入输出样例1】

    4

    2 1 3 3

    1 2 3

    1 3 4

    2 4 6

    1

    9

     

    【数据范围】

    对于20%的数据:n<=100

    对于40%的数据:n<=1000

    对于60%的数据:n<=8000

    对于80%的数据:n<=100000

    对于100%的数据:0<n<=700000;ai<=1000;1<=bi,ci<=n;di<=1000。

    数据保证一个点的加速器性能绝对小于等于它的所有的边所耗费的能量,保证所有节点都可以到达,保证没有数据与样例相同。

    【样例解释】

    如果以第一个点为根,则需要耗费0(到1)+1(到2)+2(到3)+6(到4)=9的能量值。

    如果以第二个点为根,则需要耗费2(到1)+0(到2)+4(到3)+5(到4)=11的能量值。

    如果以第三个点为根,则需要耗费1(到1)+2(到2)+0(到3)+7(到4)=10的能量值。

    如果以第四个点为根,则需要耗费5(到1)+3(到2)+7(到3)+0(到4)=15的能量值。

    很明显以第一个点为根是最优的。


     

    对每条边来说走的次数就是子树的size

    先从1开始处理一下1的答案,每次换根只会影响两个点一条边,

    只会把这条边的权值贡献从(e[i].w-w[father])*(size[son])变为(e[i].w-w[son])*(size[1]-size[son])

    所以直接用回溯遍历推出来每个点为根的答案

    有一个点错了是因为字典序的问题

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int maxn=700009;
    int n;
    struct node{
        int v,w,nxt;
    }e[maxn<<1];
    int head[maxn],cnt;
    void add(int u,int v,int w){e[++cnt].v=v;e[cnt].w=w;e[cnt].nxt=head[u];head[u]=cnt;}
    int size[maxn],w[maxn];
    ll f[maxn];
    void pre(int x,int fa){
        size[x]=1;ll tmp=0;
        for(int i=head[x];i;i=e[i].nxt){
            if(e[i].v==fa)continue;
            pre(e[i].v,x);
            f[x]+=f[e[i].v]+(e[i].w-w[x])*size[e[i].v];
            size[x]+=size[e[i].v];
        }
    }
    ll pos,ans=1e15,cost;
    void dfs(int x,int fa){
        if(ans>cost)pos=x,ans=cost;
        for(int i=head[x];i;i=e[i].nxt){
            int y=e[i].v;
            if(y==fa)continue;
            cost-=(e[i].w-w[x])*size[y],cost+=(e[i].w-w[y])*(size[1]-size[y]);
            dfs(y,x);
            cost+=(e[i].w-w[x])*size[y],cost-=(e[i].w-w[y])*(size[1]-size[y]);
        }
    }
    int main(){
        freopen("yggdrasil.in","r",stdin);
        freopen("yggdrasil.out","w",stdout);
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d",&w[i]);
        for(int i=1,u,v,w;i<n;i++){
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w);add(v,u,w);
        }
        pre(1,0);
        cost=f[1];
        dfs(1,0);
        printf("%lld
    %lld
    ",pos,ans);
    }

     

     

  • 相关阅读:
    Android混淆
    Web开发人员应当知道的15个开源项目
    应用开发10种免费推广的方法
    (转载)Comparing C++ and C (Inheritance and Virtual Functions)
    JCTVC 会议输出文档
    HEVC bit depth increasment
    函数指针声明时的形参列表可以没有
    关于链接 Linkage
    二级指针和二维数组
    C 与 C++互相调用函数,变量
  • 原文地址:https://www.cnblogs.com/superminivan/p/10808274.html
Copyright © 2020-2023  润新知