SHOI2005 树的双中心

给树 \(T=(V,E)(|V|=n)\),树高为 \(h\),\(w_u(u\in V)\)。求 \(x\in V,y\in V:\left(\sum_{u\in V}w_u\cdot \min(dis_{u,x},dis_{u,y})\right)_{\min}\)。

数据范围:\(1\le n\le 50000\),\(1\le h\le 100\)。


一眼思路:把 \(T\) 由一条边砍成 \(T_1,T_2\),\(x\) 为 \(T_1\) 重心,\(y\) 为 \(T_2\) 重心。

所以可以暴力枚举那条断边,然后找两棵树重心,合并答案。


先问个问题:一棵带点权的树怎么找重心?

洛谷P1364 医院设置

带点权树的重心 \(x\) 满足 \(f_x=\sum_{u\in V}w_u\cdot dis_{u,x}\) 最小。

暂定 \(1\) 为根,记录 \(sz_i\) 表示节点 \(i\) 的子树的权值 \(w\) 和。

所以 \(f_1=\sum_{u\in V}w_u\cdot (dep_u-dep_1)\)。

\[v\in son_u:f_v=f_u+(sz_1-sz_v)-sz_v
\]

这是换根 \(\tt dp\)。“重心”往下挪,上面的节点要多走一步,下面的节点少走一步。

然后 \(f_i\) 最小的 \(i\) 就是重心。


这题也用到了类似的思想:

令 \(g_i=\sum_{u\in subtree_i}w_u\cdot dis_{u,i}\),很明显上文的 \(f_1=g_1\)。

\[\therefore g_u=\sum_{v\in son_u}g_v+sz_v
\]

很明显吧,每个子节点答案加再走一条边的贡献。

令 \(sf_i\) 为 \(i\) 的子树最大子节点,\(sc_i\) 为 \(i\) 的子树次大子节点

设断边为 \((a,b)\),其中 \(dep_b>dep_a\)。

同样令 \(1\) 为根,同样维护 \(sz_i\)。

所以 \(T_1\) 的根为 \(1\),\(T_2\) 的根为 \(b\)。

\(\Theta(h)\) 让 \(sz_i\) 变为 \(T_1,T_2\) 内的子树权值和:

\(\forall p\in ancestor_b:sz_p-=sz_b\)

设 \(now_1=\sum_{u\in V_1}w_u\cdot dis_{u,1}=g_1-g_b-sz_b(dep_b-dep_1),now_2=\sum_{u\in V_2}w_u\cdot dis_{u,b}=g_b\)

\(T_1,T_2\) 中的节点 \(i\) 子树最大子节点必为原 \(sf_i,sc_i\) 中的一个。

再看看上面的式子:

\[v\in son_u:f_v=f_u+(sz_{rt}-sz_v)-sz_v
\]

所以 \(f_v<f_u\) 当 \(sz_{rt}<2sz_v\)。

可以从各自的根节点出发,摸着最大子树子节点找 \(T_1,T_2\) 的重心,答案可以由上面的 \(now\) 递推。


时间复杂度 \(\Theta(nh)\)。


  • 代码

上面的内容看不懂就算了,读读这代码吧。。。

#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]; //f就是g
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]; //v为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\n",ans);
return 0;
}

祝大家学习愉快!

题解-SHOI2005 树的双中心的更多相关文章

  1. 【BZOJ3302】[Shoi2005]树的双中心 DFS

    [BZOJ3302][Shoi2005]树的双中心 Description Input 第一行为N,1<N<=50000,表示树的节点数目,树的节点从1到N编号.接下来N-1行,每行两个整 ...

  2. BZOJ3302: [Shoi2005]树的双中心

    BZOJ3302: [Shoi2005]树的双中心 https://lydsy.com/JudgeOnline/problem.php?id=3302 分析: 朴素算法 : 枚举边,然后在两个连通块内 ...

  3. 【BZOJ】3302: [Shoi2005]树的双中心 && 2103: Fire 消防站 && 2447: 消防站

    [题意]给定带点权树,要求选择两个点x,y,满足所有点到这两个点中较近者的距离*点权的和最小.n<=50000,h<=100. [算法]树的重心 [题解]代码参考自:cgh_Andy 观察 ...

  4. luogu P2726 [SHOI2005]树的双中心

    传送门 强行安利->巨佬题解 如果只有一个点贡献答案,那么答案显然是这棵树的带权重心,这个是可以\(O(n)\)求的.一个\(O(n^2)\)暴力是枚举两个集合之间的分界边,然后对这两个集合分别 ...

  5. 【洛谷 P2726】 [SHOI2005]树的双中心(树的重心)

    先考虑一个\(O(N^2)\)做法. 设选的两个点为\(x,y\),则一定可以将树分成两个集合\(A,B\),使得\(A\)集合所有点都去\(x\),\(B\)集合所有点都去\(y\),而这两个集合的 ...

  6. [SHOI2005]树的双中心

    题目链接:Click here Solution: 首先我们要知道,选择两个点\(A,B\),必定存在一条边,割掉这条边,两个集合分别归\(A,B\)管 再结合题目,我们就得到了一个暴力的\(n^2\ ...

  7. bzoj 3302&2447&2103 树的双中心 树形DP

    题目: 题解: bzoj 3302 == 2447 == 2103 三倍经验 首先我们考虑枚举两个中心的位置,然后统计答案. 我们发现,一定有一部分点离第一个中心更近,另一部分点离第二个中心更近 如果 ...

  8. POJ2182题解——线段树

    POJ2182题解——线段树 2019-12-20 by juruoOIer 1.线段树简介(来源:百度百科) 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线 ...

  9. 51nod 1812 树的双直径 题解【树形DP】【贪心】

    老了-稍微麻烦一点的树形DP都想不到了. 题目描述 给定一棵树,边权是整数 \(c_i\) ,找出两条不相交的链(没有公共点),使得链长的乘积最大(链长定义为这条链上所有边的权值之和,如果这条链只有 ...

随机推荐

  1. close与shutdown

    首先看一个例子,如下图所示: 当我们客户端发送ABCD再close套接字的时候,服务器端的接收通道也被关闭了,将无法接收ABCD的数据.如果想要仅仅关闭发送通道,保留接收通道,可以使用shutdown ...

  2. POSIX信号量与互斥锁实现生产者消费者模型

    posix信号量 Link with -lpthread. sem_t *sem_open(const char *name, int oflag);//打开POSIX信号量 sem_t *sem_o ...

  3. logback怎么写?分类输出日志到不同的文件

    此appender有顺序,最好不要乱调顺序,输出日志如下: drwxr-xr-x 2 root root 4096 Dec 3 00:00 2019-12-02drwxr-xr-x 2 root ro ...

  4. http请求返回ObjectJson,Array之类转换类型

    以下所说的类来自:package com.alibaba.fastjson 1,形如以下返回,其实是个json的map形式的返回 { "success": true, " ...

  5. ubuntu配置bonding

    如果节点上有多个网络接口时可以通过bonding将多个网络接口虚拟为一个网络接口,bonding可以提供高可用及负载均衡功能,从而提高节点的网络接口性能及可用性. 配置单bond 一.使用如下命令安装 ...

  6. Django实战总结 - 快速开发一个数据库查询工具

    一.简介 Django 是一个开放源代码的 Web 应用框架,由 Python 写成. Django 只要很少的代码就可以轻松地完成一个正式网站所需要的大部分内容,并进一步开发出全功能的 Web 服务 ...

  7. html 小米商城导航栏示例

    1.小米导航栏示例 <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset=&q ...

  8. 死磕以太坊源码分析之Kademlia算法

    死磕以太坊源码分析之Kademlia算法 KAD 算法概述 Kademlia是一种点对点分布式哈希表(DHT),它在容易出错的环境中也具有可证明的一致性和性能.使用一种基于异或指标的拓扑结构来路由查询 ...

  9. LeetCode 中等题解(2)

    31 下一个排列 Question 实现获取下一个排列的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列. 如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列). 必须 ...

  10. Vegas的软对比具体如何设置

    软对比是Vegas中比较常用的一个视频特效,通过这一特效可以让视频画面更加柔和.而对于新手用户来说,软对比显得有些陌生,不知道该如何使用. 本集主要为大家介绍:sony vegas的软对比. 在视频制 ...