题解-SHOI2005 树的双中心
Posted wendigo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解-SHOI2005 树的双中心相关的知识,希望对你有一定的参考价值。
给树 (T=(V,E)(|V|=n)),(w_u(uin V))。求 (xin V,yin V:left(sum_{uin V}w_ucdot min(dis_{u,x},dis_{u,y}) ight)_{min})。
数据范围:(1le nle 50000),(1le T‘s~Heightle 100)。
一眼思路:把 (T) 由一条边砍成 (T_1,T_2),(x) 为 (T_1) 重心,(y) 为 (T_2) 重心。
所以可以暴力枚举那条断边,然后找两棵树重心,合并答案。
(color{#ff0000}{ t [1]}):一棵带点权的树怎么找重心?
即 洛谷P1364 医院设置。
带点权树的重心 (x) 满足 (f_x=sum_{uin V}w_ucdot dis_{u,x}) 最小。
暂定 (1) 为根,记录 (sz_i) 表示节点 (i) 的子树的权值 (w) 和。
所以 (f_1=sum_{uin V}w_ucdot (dep_u-dep_1))。
这是换根 ( t dp)。“重心”往下挪,上面的节点要多走一步,下面的节点少走一步。
然后 (f_i) 最小的 (i) 就是重心。
(color{#ffaa00}{ t [2]}):(T_1,T_2) 如何快速求重心?
设断边为 ((u,v)),其中 (dep_v>dep_u)。
同样令 (1) 为根,所以 (T_1) 的根为 (1),(T_2) 的根为 (v),同样维护 (sz_i)。
令 (g_i=sum_{uin subtree_i}w_ucdot dis_{u,i}),很明显上文的 (f_1=g_1)。
很明显吧,每个子节点答案加再走一条边的贡献。
未完待续
#include <bits/stdc++.h>
using namespace std;
//Start
typedef long long ll;
typedef double db;
#define mp(a,b) make_pair(a,b)
#define x first
#define y second
#define b(a) a.begin()
#define e(a) a.end()
#define sz(a) int((a).size())
#define pb(a) push_back(a)
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
//Data
const int N=5e4;
int n,w[N+7];
vector<int> e[N+7];
//TreeDP
int dep[N+7],sz[N+7],fa[N+7],f[N+7],sf[N+7],sc[N+7];
void Dfs1(int u){
sz[u]=w[u],dep[u]=dep[fa[u]]+1;
for(int&v:e[u])if(v!=fa[u]){
fa[v]=u,Dfs1(v),sz[u]+=sz[v],f[u]+=f[v]+sz[v];
if(sz[v]>sz[sf[u]]) sc[u]=sf[u],sf[u]=v;
else if(sz[v]>sz[sc[u]]) sc[u]=v;
}
}
int cut;
void Dfs2(int u,int now,int sm,int&res){
res=min(res,now);
int v=(sf[u]==cut||sz[sc[u]]>sz[sf[u]])?sc[u]:sf[u];
if(v&&2*sz[v]>sm) Dfs2(v,now+sm-2*sz[v],sm,res);
}
void Dfs3(int u,int&res){
for(int&v:e[u])if(v!=fa[u]){
cut=v;
int up=inf,down=inf;
for(int p=u;p;p=fa[p]) sz[p]-=sz[v];
Dfs2(1,f[1]-f[v]-(dep[v]-dep[1])*sz[v],sz[1],up);
Dfs2(v,f[v],sz[v],down);
res=min(res,up+down);
for(int p=u;p;p=fa[p]) sz[p]+=sz[v];
Dfs3(v,res);
}
}
//Main
int main(){
scanf("%d",&n);
for(int i=1,u,v;i<=n-1;i++) scanf("%d%d",&u,&v),e[u].pb(v),e[v].pb(u);
for(int i=1;i<=n;i++) scanf("%d",&w[i]);
int ans=inf; Dfs1(1),Dfs3(1,ans),printf("%d
",ans);
return 0;
}
以上是关于题解-SHOI2005 树的双中心的主要内容,如果未能解决你的问题,请参考以下文章