枚举每条树边,将其断开,那么两侧肯定取带权重心最优。

考虑如何求出每个子树的重心,枚举其所有儿子,通过重量关系就可以判断出重心位于哪棵子树。

然后将那棵子树的重心暴力往上爬即可,因为每个点作为重心肯定是一段连续的链,所以复杂度为$O(n)$。

然后就是如何求出砍掉每棵子树之后剩下的部分的重心。

设当前点到根的路径为关键路径,那么可以通过二分求出重心在关键路径上哪个点的子树里。

对于那棵子树,重心要么是它本身,要么在它最重的子树里,要么在次重的子树里。

在线段树上按dfs序维护区间内子树重量的最大值,即可用线段树完成重心的查询,时间复杂度$O(\log n)$。

总时间复杂度$O(n\log n)$。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=500010,BUF=12000000;
char Buf[BUF],*buf=Buf;
int n,i,x,y,w[N],g[N],v[N<<1],nxt[N<<1],ed;
int f[N],d[N],size[N],son[N],top[N],st[N],en[N],id[N],dfn,q[N],cq;
int fir[N],sec[N],center[N],vip[N];
ll sum[N],sw[N],val[1050000],sd[N],su[N],ans=1LL<<60;
inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;}
inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;}
void dfs(int x){
size[x]=1;
for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){
f[v[i]]=x,d[v[i]]=d[x]+1;
dfs(v[i]),size[x]+=size[v[i]];
if(size[v[i]]>size[son[x]])son[x]=v[i];
}
}
void dfs2(int x,int y){
id[st[x]=++dfn]=x;top[x]=y;
if(son[x])dfs2(son[x],y);
for(int i=g[x];i;i=nxt[i])if(v[i]!=son[x]&&v[i]!=f[x])dfs2(v[i],v[i]);
en[x]=dfn;
}
inline int dis(int x,int y){
int t=d[x]+d[y];
for(;top[x]!=top[y];x=f[top[x]])if(d[top[x]]<d[top[y]])swap(x,y);
if(d[x]>d[y])swap(x,y);
return t-2*d[x];
}
inline void cal(int x){
int i=center[fir[x]];
ll t=sum[fir[x]]+sd[x]-sd[fir[x]]-sw[fir[x]]+(sw[x]-sw[fir[x]])*(d[i]-d[x]);
while(2*sw[i]<sw[x])t+=2*sw[i]-sw[x],i=f[i];
center[x]=i;
sum[x]=t;
}
void dfs3(int x){
sw[x]=w[x];
for(int i=g[x];i;i=nxt[i]){
int y=v[i];
if(y==f[x])continue;
dfs3(y);
sw[x]+=sw[y];
if(sw[y]>sw[fir[x]])sec[x]=fir[x],fir[x]=y;
else if(sw[y]>sw[sec[x]])sec[x]=y;
sd[x]+=sd[y]+sw[y];
}
if(2*sw[fir[x]]<=sw[x]){
center[x]=x;
sum[x]=sd[x];
return;
}
cal(x);
}
void dfs4(int x){
if(f[x]){
int y=f[x];
su[x]=su[y]+sd[y]-sd[x]-2*sw[x]+sw[1];
}
for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x])dfs4(v[i]);
}
inline int lower(ll x){
int l=2,r=cq,mid,t=1;
while(l<=r)if(2*(sw[q[mid=(l+r)>>1]]-x)>=sw[1]-x)l=(t=mid)+1;else r=mid-1;
return q[t];
}
void build(int x,int a,int b){
if(a==b){val[x]=sw[id[a]]*2;return;}
int mid=(a+b)>>1;
build(x<<1,a,mid),build(x<<1|1,mid+1,b);
val[x]=max(val[x<<1],val[x<<1|1]);
}
int ask(int x,int a,int b,int c,int d,ll p){
if(val[x]<p)return 0;
if(a==b)return a;
int mid=(a+b)>>1,t=0;
if(d>mid)t=ask(x<<1|1,mid+1,b,c,d,p);
if(t)return t;
if(c<=mid)t=ask(x<<1,a,mid,c,d,p);
return t;
}
void dfs5(int x){
if(f[x])vip[x]=lower(sw[x]);
q[++cq]=x;
for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x])dfs5(v[i]);
cq--;
}
inline void solve(int x){
int t=vip[x],y,z=0;
if(st[fir[t]]<=st[x]&&en[x]<=en[fir[t]])y=sec[t];else y=fir[t];
if(y)z=ask(1,1,n,st[y],en[y],sw[1]-sw[x]);
if(!z)z=t;else z=id[z];
ans=min(ans,sum[x]+sd[z]+su[z]-sd[x]-sw[x]*dis(x,z));
}
int main(){
fread(Buf,1,BUF,stdin);read(n);
for(i=1;i<n;i++)read(x),read(y),add(x,y),add(y,x);
for(i=1;i<=n;i++)read(w[i]);
dfs(1);dfs2(1,1);
dfs3(1);dfs4(1);
build(1,1,n);dfs5(1);
for(i=2;i<=n;i++)solve(i);
return printf("%lld",ans),0;
}

  

BZOJ3068 : 小白树的更多相关文章

  1. bzoj AC倒序

    Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...

  2. Bzoj 1756: Vijos1083 小白逛公园 线段树

    1756: Vijos1083 小白逛公园 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 1021  Solved: 326[Submit][Statu ...

  3. Vijos 1083 小白逛公园(线段树)

    线段树,每个结点维护区间内的最大值M,和sum,最大前缀和lm,最大后缀和rm. 若要求区间为[a,b],则答案max(此区间M,左儿子M,右儿子M,左儿子rm+右儿子lm). ----------- ...

  4. 线段树 || BZOJ1756: Vijos1083 小白逛公园 || P4513 小白逛公园

    题面:小白逛公园 题解: 对于线段树的每个节点除了普通线段树该维护的东西以外,额外维护lsum(与左端点相连的最大连续区间和).rsum(同理)和sum……就行了 代码: #include<cs ...

  5. 树的最长链-POJ 1985 树的直径(最长链)+牛客小白月赛6-桃花

    求树直径的方法在此转载一下大佬们的分析: 可以随便选择一个点开始进行bfs或者dfs,从而找到离该点最远的那个点(可以证明,离树上任意一点最远的点一定是树的某条直径的两端点之一:树的直径:树上的最长简 ...

  6. 洛谷 P4513 小白逛公园-区间最大子段和-分治+线段树区间合并(单点更新、区间查询)

    P4513 小白逛公园 题目背景 小新经常陪小白去公园玩,也就是所谓的遛狗啦… 题目描述 在小新家附近有一条“公园路”,路的一边从南到北依次排着nn个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩 ...

  7. 牛客网 牛客小白月赛5 I.区间 (interval)-线段树 or 差分数组?

    牛客小白月赛5 I.区间 (interval) 休闲的时候写的,但是写的心情有点挫,都是完全版线段树,我的一个队友直接就水过去了,为啥我的就超内存呢??? 试了一晚上,找出来了,多初始化了add标记数 ...

  8. 牛客小白月赛19 E 「火」烈火燎原 (思维,树)

    牛客小白月赛19 E 「火」烈火燎原 (思维,树) 链接:https://ac.nowcoder.com/acm/contest/2272/E来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空 ...

  9. [vijos]1083小白逛公园<线段树>

    描述 小新经常陪小白去公园玩,也就是所谓的遛狗啦…在小新家附近有一条“公园路”,路的一边从南到北依次排着n个公园,小白早就看花了眼,自己也不清楚该去哪些公园玩了. 一开始,小白就根据公园的风景给每个公 ...

随机推荐

  1. 原始套接字SOCK_RAW

    原始套接字SOCK_RAW 实际上,我们常用的网络编程都是在应用层的报文的收发操作,也就是大多数程序员接触到的流式套接字(SOCK_STREAM)和数据包式套接字(SOCK_DGRAM).而这些数据包 ...

  2. js冒泡排序与二分法查找

    冒泡排序 var attr=[1,5,7,6,3,9,2,8,4]; var zj=0; //控制比较轮数 for(var i=0;i<attr.length-1;i++) { //控制每轮的比 ...

  3. ASP.NET Web Api 安全性(转载)

    转载地址:http://www.cnblogs.com/fzrain/p/3552423.html 在Web Api中强制使用Https 我们可以在IIS级别配置整个Web Api来强制使用Https ...

  4. Android浏览本地 API文档 + 解决页面加载慢的问题

    火狐浏览器安装离线浏览插件: 用浏览器打开index.html文件,你会发现加载的很慢,原因你懂的,为此,我们可以通过离线的方式 查看本地API文档,用火狐浏览器  +   Work Offline插 ...

  5. Python 的三目运算

    其他语言:php 判定条件?为真时的结果:为假时的结果 $a=88 $b=99 $res = $a>$b?$a>$b 搞笑的Python:令人意想不到的语法形式 true_value if ...

  6. 【stut 逆置正整数】

    C语言实验——逆置正整数 Time Limit: 1000ms   Memory limit: 65536K  有疑问?点这里^_^ 题目描述 输入一个三位正整数,将它反向输出. 输入 3位正整数. ...

  7. RTP 与RTCP 解释. 含同步时间戳

    转自:http://blog.csdn.net/wudebao5220150/article/details/13816225 RTP协议是real-time transport protocol的缩 ...

  8. MicroService/web Service/webAPI/RPC

    [TOC] 微服务 服务拆分,利用轻量化机制(通常为HTTP源API)实现通信,复杂度可控,独立部署,技术选型灵活,容错,扩展. 康威定律的实际体现 微服务架构模式深刻影响了应用和数据库之间的关系,不 ...

  9. 什么是好的API设计?(转)

    什么是API? 我们只要是在进行编程我们就需要不停的设计API. API简单来讲可以是一个调用的函数,一个接口. 抽象来说,接口是一个内聚系统暴漏给外部的一切信息,包含但不限于: 调用方式:比如通过l ...

  10. 利用Aspose.Word控件和Aspose.Cell控件,实现Word文档和Excel文档的模板化导出

    我们知道,一般都导出的Word文档或者Excel文档,基本上分为两类,一类是动态生成全部文档的内容方式,一种是基于固定模板化的内容输出,后者在很多场合用的比较多,这也是企业报表规范化的一个体现. 我的 ...