码迷,mamicode.com
首页 > 其他好文 > 详细

不知哪个OJ 小x游世界树 换根?不知。

时间:2019-05-04 16:12:24      阅读:147      评论:0      收藏:0      [点我收藏+]

标签:oid   自己   void   return   个数   amp   aci   如何   while   

小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的能量值。

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

 

好吧很明显的树上操作。。。考试是不知刚开始打了写什么辣鸡玩意QWQ

先求出来以1(随便哪个都行)为根的代价,具体地,把他当作根,做一遍dfs,每条边的贡献是(w(u,v)-a[u])*size[v];

然后主要就是在树上如何转移:由u到v时,当前的cost(cost初值是以1为根的代价)-= (w(u,v)-a[u])*size[v] 再+=(w(u,v)-a[v])*(sz[1]-sz[v]),相当于是原来是从u到v,现在是从v到u,回溯时反向操作。

然后更新答案时记得加上字典序的比较,否则会死(亲身经历QWQ)

#include<cstdio>
#include<iostream>
#define ll long long
#define R register ll
using namespace std;
const int N=700010;
inline int g() {
    R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch==-?-1:fix;
    do ret=ret*10+(ch^48); while(isdigit(ch=getchar())); return ret*fix;
}
int n,cnt,ccnt,anss;
int vr[N<<1],nxt[N<<1],w[N<<1],fir[N],sz[N],a[N];
ll cost,ans=1E+15;
inline void add(int u,int v,int ww) {vr[++cnt]=v,w[cnt]=ww,nxt[cnt]=fir[u],fir[u]=cnt;}
inline void dfsdown(int u,int fa) { sz[u]=1;
    for(R i=fir[u];i;i=nxt[i]) { R v=vr[i];
        if(v==fa) continue; 
        dfsdown(v,u); sz[u]+=sz[v]; cost+=(w[i]-a[u])*sz[v];
    } 
} 
inline void solve(int u,int fa) {
    if(ans>cost||(ans==cost&&anss>u)) anss=u,ans=cost;
    for(R i=fir[u];i;i=nxt[i]) { R v=vr[i];
        if(v==fa) continue;
        cost-=(w[i]-a[u])*sz[v],cost+=(w[i]-a[v])*(sz[1]-sz[v]); solve(v,u);
        cost+=(w[i]-a[u])*sz[v],cost-=(w[i]-a[v])*(sz[1]-sz[v]);
    }
}
signed main() { freopen("yggdrasil.in","r",stdin); freopen("out.out","w",stdout);
    n=g(); for(R i=1;i<=n;++i) a[i]=g(); 
    for(R i=1,u,v,w;i<n;++i)  u=g(),v=g(),w=g(),add(u,v,w),add(v,u,w);
    dfsdown(1,0); solve(1,0); printf("%d\n%lld\n",anss,ans);
} 

 

因为听了BK老师的课而自闭了几天。。。今天考试智商下降体现在下一题上。。。QWQ2019.05.04

不知哪个OJ 小x游世界树 换根?不知。

标签:oid   自己   void   return   个数   amp   aci   如何   while   

原文地址:https://www.cnblogs.com/Jackpei/p/10808723.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!