博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【洛谷 P2726】 [SHOI2005]树的双中心(树的重心)
阅读量:7122 次
发布时间:2019-06-28

本文共 2843 字,大约阅读时间需要 9 分钟。

先考虑一个\(O(N^2)\)做法。

设选的两个点为\(x,y\),则一定可以将树分成两个集合\(A,B\),使得\(A\)集合所有点都去\(x\)\(B\)集合所有点都去\(y\),而这两个集合的分界点就是树上的一条边。于是考虑枚举断哪条边,然后对两边分别跑一遍带权树的重心,统计答案加起来取最小值就行了。

现在进行优化,求树的重心的方法可以参考。

\(1\)为根建树,\(f[u]\)表示所有点到\(u\)的总距离。(人数乘以距离)

转移方程是:

\[f[v]=f[u]+size[1]-size[v]-size[v]\]
可以发现,只有当\(size[v]*2>size[1]\)\(v\)\(u\)更优,而且满足\(size[v]*2>size[1]\)\(v\)数量\(<=1\)

所以我们可以预处理出每个点的子树大小最大的儿子和次大的儿子,每次断边时自下而上修改其所有祖先的\(size\)大小,这时最大儿子可能变小,进而被次大儿子替代,直接判断一下然后走此时的大儿子就行。易得时间复杂度为\(O(NH)\),这也就是题中提到树的高度不超过\(100\)的原因吧。

#include 
#include
#define INF 2147483647using namespace std;#define Open(s) freopen(s".in","r",stdin);freopen(s".out","w",stdout);#define Close fclose(stdin);fclose(stdout);int s, w; char ch;inline int read(){ s = 0; ch = getchar(); while(ch < '0' || ch > '9') ch = getchar(); while(ch >= '0' && ch <= '9'){ s = s * 10 + ch - '0'; ch = getchar();} return s;}const int MAXN = 100010;struct Edge{ int next, to;}e[MAXN << 1];int head[MAXN], num, a[MAXN], size[MAXN], f[MAXN], val[MAXN], dep[MAXN], son[MAXN], Sson[MAXN], A, B, n, root, ans = INF, cut;inline void Add(int from, int to){ e[++num].to = to; e[num].next = head[from]; head[from] = num; e[++num].to = from; e[num].next = head[to]; head[to] = num;}int getsize(int u, int fa){ size[u] = a[u]; f[u] = fa; dep[u] = dep[fa] + 1; for(int i = head[u]; i; i = e[i].next) if(e[i].to != fa){ getsize(e[i].to, u); size[u] += size[e[i].to]; val[u] += val[e[i].to] + size[e[i].to]; if(size[e[i].to] > size[son[u]]){ Sson[u] = son[u]; son[u] = e[i].to; } else if(size[e[i].to] > size[Sson[u]]) Sson[u] = e[i].to; }}void getans(int u, int now, int all, int &res){ res = min(res, now); int v = son[u]; if(v == cut || size[Sson[u]] > size[son[u]]) v = Sson[u]; //如果size变化后次大大于最大 if(!v) return; if(size[v] * 2 > all) getans(v, now + all - size[v] - size[v], all, res); //如果size[v]*2<=all就没有继续往下走的意义了,因为此时u一定最优}int solve(int u){ for(int i = head[u]; i; i = e[i].next) if(e[i].to != f[u]){ cut = e[i].to; //断边 A = B = INF; for(int now = u; now; now = f[now]) size[now] -= size[e[i].to]; //自下而上修改其父亲size getans(1, val[1] - val[e[i].to] - dep[e[i].to] * size[e[i].to], size[1], A); getans(e[i].to, val[e[i].to], size[e[i].to], B); //求两个集合的答案 ans = min(ans, A + B); for(int now = u; now; now = f[now]) size[now] += size[e[i].to]; //回溯 solve(e[i].to); }}int main(){ //Open("practice"); n = read(); dep[0] = -1; for(int i = 1; i < n; ++i) Add(read(), read()); for(int i = 1; i <= n; ++i) a[i] = read(); getsize(1, 0); solve(1); printf("%d\n", ans); return 0;}

转载于:https://www.cnblogs.com/Qihoo360/p/9871320.html

你可能感兴趣的文章
eclipse中将Maven Dependencies Libraries移除后的恢复办法
查看>>
mysql 插入-更新-删除
查看>>
C++语言笔记C11库
查看>>
systemd及启动流程
查看>>
java转换ppt,ppt转成图片,获取备注,获取文本
查看>>
lvs 负载均衡fullnat 模式clientip 怎样传递给 realserver
查看>>
python实现FTP服务器
查看>>
负载均衡7层nginx(提供软件包)
查看>>
python 数据类型学习
查看>>
Hello,World
查看>>
Linux的用户和组命令之groupmod
查看>>
在windows上秒开应用程序
查看>>
HTML快速入门4
查看>>
JQUERY中字符串和JSON的转换
查看>>
三句话告诉你 mapreduce 中MAP进程的数量怎么控制?
查看>>
wxWidgets第十六课 wxTimer没有调用stop导致崩溃的问题分析
查看>>
centos7.x rsync+inotify实时监控备份
查看>>
LNMP环境下的Nagios搭建
查看>>
spring ioc 注入List/Map等
查看>>
5.理想中的Redis5.1 第二代Codis
查看>>