[算法学习] 换根dp
换根dp
一般来说,我们做题的树都是默认 \(1\) 为根的。但是有些题目需要计算以每个节点为根时的内容。
朴素的暴力:以每个点 \(u\) 作为 \(root\) 暴力dfs下去,复杂度\(O(n^2)\);
正确的做法:换根dp,复杂度\(O(n)\)。
执行步骤
- 第一次扫描,先默认 \(root=1\) ,跑一遍 \(dfs\);
- 第二次扫描,从 \(root=1\) 开始,每次从 \(u\) 到 \(v\) 节点时,计算根从 \(u\) 转移到 \(v\) 时的贡献变化。
很显然,换根dp是在两个\(dfs\)中完成的,下面我们介绍一下如何运用它。
例题1 Accumulation Degree
题目链接:South Central China 2008 Accumulation Degree
Description
给你一颗有 \(n\) 个节点的树,每一条边连接 \(u_i\) 和 \(v_i\),流量为 \(fl_i\) ,你需要找出一个点作为 \(root\),并最大化从该点出发到所有叶子节点的流量最大值。
多组数据。(PS:题意读不懂的可以结合题目中的图理解,类似网络流的流法)
数据范围 \(1 \le n\le 200000\),并且 \(\sum n \le 200000\)
时间限制 \(1000\ ms\)
Solution
我们先默认这棵树以 \(1\) 为根,跑一次 \(dfs\)。
定义 \(flow[i]\) 表示以 \(i\) 为根的子树中流量最大值。
那么,\(u\) 节点从儿子 \(v\) 得到的流量为:
1.若\(v\)为叶子节点,那么\(flow[u] += flow[v]\)(可以直接流过来);
2.若\(v\)为非叶子节点,那么\(flow[u] += min(flow[v], fl(u, v))\)(\(u\)和\(v\)相连的边有流量限制)。
这样,我们得到了以 \(1\) 为根时的答案,记为 \(f[1]\),它的值等于 \(flow[1]\)。
考虑如何换根。
从 \(u\) 为根转移到儿子 \(v\) 为根, \(f[v]\) 包括两部分:一部分是从 \(v\) 流向自己的子树,一部分是从 \(v\) 往父节点走。
那么贡献的变化是第二部分造成的,原本的贡献是 \(flow[u] - min(flow[v], fl(u, v))\),现在加上 \(u\) 到 \(v\) 这条边的流量限制,所以新的贡献是 \(min(fl(u, v), flow[u] - min(flow[v], fl(u, v)))\)。
注意如果 \(u\) 的度为 \(1\),则需要特殊处理。
再来一个 \(dfs\) 转移即可。
复杂度 \(O(n)\),可以通过本题。
Code
例题2 STA-Station
题目链接:POI2008 STA-Station
Description
给你一颗有 \(n\) 个节点的树,你需要找出一个点作为 \(root\) ,并最大化 \(\sum_{i=1}^{n} dep_i\)。
其中 \(dep_i\) 表示以 \(root\) 为根时,\(i\)节点的深度。
数据范围 \(1\le n\le 10^6\)
时间限制 \(1000\ ms\)
Solution
我们先默认这颗树以 \(1\) 为根,跑一次 \(dfs\),记录\(dep[i]\) 和\(size[i]\)。
接下来,定义 \(f_i\) 表示以 \(i\) 为根时的 \(dep[i]\) 之和。
显然,\(f[1] = \sum_{i=1}^{n} dep[i]\)。
当我们从 \(u\) 转移到儿子 \(v\) 时,以 \(v\) 为根的子树内的所有节点 \(dep\) 值都减一,以外的所有节点 \(dep\) 值都加一。
于是有: \(f[v] = f[u] - size[v] + (n - size[v]) = f[u] + n - 2 * size[v]\)。
答案即为 \(max_{i=1}^{n} f[i]\) 的 \(i\)。
复杂度 \(O(n)\),卡卡常可以通过本题。
Code
这个题目卡\(vector\),能把用\(STL\)的完美卡飞。所以我改成前向星了呜呜呜。
// Author: wlzhouzhuan
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define rint register int
#define rep(i, l, r) for (rint i = l; i <= r; i++)
#define per(i, l, r) for (rint i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define mp(a, b) make_pair(a, b)
#define Each(i) for (rint i = head[u]; i; i = edge[i].nxt)
inline int read() {
int x = 0, neg = 1; char op = getchar();
while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
return neg * x;
}
inline void print(int x) {
if (x < 0) { putchar('-'); x = -x; }
if (x >= 10) print(x / 10);
putchar(x % 10 + '0');
}
const int N = 1000005;
struct Edge {
int to, nxt;
} edge[N << 1];
int head[N], tot;
void add(int u, int v) {
edge[++tot] = {v, head[u]};
head[u] = tot;
}
int n;
ll f[N];
int sz[N], dep[N];
void dfs1(int u, int fa) {
sz[u] = 1;
dep[u] = dep[fa] + 1;
Each(i) {
int v = edge[i].to;
if (v == fa) continue;
dfs1(v, u);
sz[u] += sz[v];
}
}
void dfs2(int u, int fa) {
Each(i) {
int v = edge[i].to;
if (v == fa) continue;
f[v] = f[u] + n - 2ll * sz[v];
dfs2(v, u);
}
}
int main() {
ios :: sync_with_stdio(false); cin.tie(0);
cin >> n;
for (int i = 1; i < n; i++) {
int u, v;
cin >> u >> v;
add(u, v), add(v, u);
}
dfs1(1, 0);
for (int i = 1; i <= n; i++) f[1] += dep[i];
dfs2(1, 0);
cout << max_element(f + 1, f + n + 1) - f << '\n';
return 0;
}
[算法学习] 换根dp的更多相关文章
- 换根 DP 学习笔记
前言 没脑子选手什么都不会. 正文 先来写一下换根 DP 的特点或应用方面: 不同的点作为树的根节点,答案不一样. 求解答案时要求出每一个节点的信息. 无法通过一次搜索完成答案的求解,因为一次搜索只能 ...
- 【换根DP】小奇的仓库
题目背景 小奇采的矿实在太多了,它准备在喵星系建个矿石仓库.令它无语的是,喵星系的货运飞船引擎还停留在上元时代! 题目内容 喵星系有\(n\)个星球,星球以及星球间的航线形成一棵树. 从星球\(a\) ...
- [BZOJ4379][POI2015]Modernizacja autostrady[树的直径+换根dp]
题意 给定一棵 \(n\) 个节点的树,可以断掉一条边再连接任意两个点,询问新构成的树的直径的最小和最大值. \(n\leq 5\times 10^5\) . 分析 记断掉一条边之后两棵树的直径为 \ ...
- 2018.10.15 NOIP训练 水流成河(换根dp)
传送门 换根dp入门题. 貌似李煜东的书上讲过? 不记得了. 先推出以1为根时的答案. 然后考虑向儿子转移. 我们记f[p]f[p]f[p]表示原树中以ppp为根的子树的答案. g[p]g[p]g[p ...
- 换根DP+树的直径【洛谷P3761】 [TJOI2017]城市
P3761 [TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速公 ...
- 小奇的仓库:换根dp
一道很好的换根dp题.考场上现场yy十分愉快 给定树,求每个点的到其它所有点的距离异或上m之后的值,n=100000,m<=16 只能线性复杂度求解,m又小得奇怪.或者带一个log像kx一样打一 ...
- 国家集训队 Crash 的文明世界(第二类斯特林数+换根dp)
题意 题目链接:https://www.luogu.org/problem/P4827 给定一棵 \(n\) 个节点的树和一个常数 \(k\) ,对于树上的每一个节点 \(i\) ,求出 \( ...
- Acesrc and Travel(2019年杭电多校第八场06+HDU6662+换根dp)
题目链接 传送门 题意 两个绝顶聪明的人在树上玩博弈,规则是轮流选择下一个要到达的点,每达到一个点时,先手和后手分别获得\(a_i,b_i\)(到达这个点时两个人都会获得)的权值,已经经过的点无法再次 ...
- bzoj 3566: [SHOI2014]概率充电器 数学期望+换根dp
题意:给定一颗树,树上每个点通电概率为 $q[i]$%,每条边通电的概率为 $p[i]$%,求期望充入电的点的个数. 期望在任何时候都具有线性性,所以可以分别求每个点通电的概率(这种情况下期望=概率 ...
随机推荐
- 通读Python官方文档之wsgiref(未完成)
wsgirf-WSGI功能及参考实现 源码:Lib/wsgiref Web服务器网关接口(Web Server Gateway Interface, WSGI),是用Python写的一个服务器软件和w ...
- javaweb图书管理系统之账号密码验证登录
验证账号与密码是否正确功能 一.注册功能 首先,在验证账号与密码是否正确的前提下的,需要先注册一个账号,如果没有账号,就会进不去,也无法验证. 其实,注册功能就是一个添加的功能,仿照我的第一篇文章,往 ...
- Canvas 制作海报
HTML <template> <view class="content"> <view class="flex_row_c_c mod ...
- 数据库number(4,3)表示什么
1 你看 number(4,3)是表示 这个数 一共有4位是有效位,后面的3 表示有3个是小数也就是这个数 只能是1.234,这样格式的 最大只能是9.999,2 number(3,4) 表示这个数 ...
- react和react-dom是什么?
使用react开发网页的话,我们难免会下载两个包,一个是react,一个是react-dom,其中react是react的核心代码.react的核心思想是虚拟Dom,其实虚拟Dom改变没有那么复杂,简 ...
- caioj 1000: [视频]整数运算[水题]
题目大意:输入两个整数a和b,输出他们的和. 题解:水题不用题解,简单看一下就知道了-- 代码: #include <cstdio> int a, b; int main() { whil ...
- Postman中文版客户端
"新冷战"蔓延到生产力工具 前端时间,著名 UI 设计软件 Figma 宣布制裁大疆! 这不仅让中国的互联网从业者纷纷一头冷汗:今天是 Figma,明天会是什么?今天是大疆,明天会 ...
- vue--vuex 状态管理模式
前言 vuex作为vue的核心插件,同时在开发中也是必不可少的基础模块,本文来总结一下相关知识点. 正文 1.基于单向数据流问题而产生了Vuex 单向数据流是vue 中父子组件的核心概念,props ...
- 基于HiKariCP组件,分析连接池原理
HiKariCP作为SpringBoot2框架的默认连接池,号称是跑的最快的连接池,数据库连接池与之前两篇提到的线程池和对象池,从设计的原理上都是基于池化思想,只是在实现方式上有各自的特点:
- HMS Core 6.4.0版本发布公告
支持转化事件回传至华为应用市场商业推广,便捷归因,实时调优: 卸载分析新增卸载前路径分析,深度剖析卸载根因. 查看详情 新增关键帧能力,通过关键帧点可实现图片.文字等位置移动.旋转等动态效果,比如文字 ...