DFS序专题
牛客专题之DFS序
简介
dfs序:
每个节点在dfs
深度优先遍历中的进出栈的时间序列,也就是tarjan
算法中的dfn
数组。
画个图理解一下:
这棵树的dfs
序:1 3 2 4 2 5 6 7 6 5 1
。
那么这个序列有什么用呢?
通过观察,两个相同数字之间就是以它为根的子树
, 也就是说,通过dfs
序我们可以得到,这个节点第一次进入栈的时间戳\(l_i\)和第一次出栈的时间戳\(r_i\)。之后我们就可以通过\(l_i\)和\(r_i\)操纵这棵树了。
具体看题:
模板
Military Problem 原CF1006E
题意
你有一棵有\(n\)个节点的树,有\(q\)次询问,每次询问有\((u,k)\),指从以\(u\)为根的子树出发先序遍历到达的第\(k\)个点是哪一个?如果不存在,输出\(-1\)。
一道模板题。先预处理出每个点的dfs
序, 即每个点的\(l_i\)和\(r_i\)。
询问的时候判断是否\(l[u] + k - 1 > r[u]\), 否则输出\(a[l[u]+k-1]\)就好了。
/*
https://ac.nowcoder.com/acm/problem/112932
https://codeforces.com/problemset/problem/1006/E
*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mod = 1e9 + 7;
const int maxn = 2e5 + 10;
int a[maxn], cnt = 0;
int l[maxn], r[maxn];
vector<int> G[maxn];
void dfs(int u, int fa){
l[u] = ++cnt;
a[cnt] = u;
for(auto v : G[u]) {
if(fa == v) continue;
dfs(v, u);
}
r[u] = cnt;
}
int main(){
int n, q;
cin >> n >> q;
for(int i = 2; i <= n; i++) {
int u; cin >> u;
G[i].push_back(u);
G[u].push_back(i);
}
dfs(1, 0);
for(int i = 1; i <= q; i++){
int u, k;
cin >> u >> k;
if(l[u] + k - 1 > r[u]) cout << -1 << endl;
else cout << a[l[u] + k - 1] << endl;
}
return 0;
}
选点
题意
有一棵\(n\)个节点的二叉树,每个节点有权值\(w_i\),要选尽量多的点,但是得满足以下限制:
对于任意一棵子树,都要满足:
- 如果选了根节点的话,在这棵子树内选的其他的点都要比根节点的值大
- 如果在左子树选了一个点,在右子树中选的其他点要比它小。
思路
读题后可以得到:根的值< 右子树的最大值 < 左子树的最大值, 如果要得到尽量多的点,只需要记录从根 -> 右子树 -> 左子树的dfs
序(此处为入栈时间戳\(l_i\)), 在\(l_i\)上找最长上升子序列(没想到。。)即可。
代码
/*
https://ac.nowcoder.com/acm/problem/22494
*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int mod = 1e9 + 7;
const int maxn = 2e5 + 10;
int a[maxn], tot = 0, cnt;
int l[maxn], r[maxn], w[maxn], ans[maxn];
vector<int> G[maxn];
void dfs(int u){
if(u == 0) return;
a[++tot] = w[u];
dfs(r[u]); dfs(l[u]);
}
int main(){
int n;
cin >> n;
for(int i = 1; i <= n; i++) cin >> w[i];
for(int i = 1; i <= n; i++) cin >> l[i] >> r[i];
dfs(1);
ans[++cnt] = a[1];
// 最长上升子序列贪心O(nlogn)解法
for(int i = 2; i <= n; i++){
if(a[i] > ans[cnt]) ans[++cnt] = a[i];
else{
int t = lower_bound(ans + 1, ans + 1 + cnt, a[i]) - ans;
ans[t] = a[i];
}
}
cout << cnt << endl;
return 0;
}
dfs序+树状数组/线段树
求和
题意
一颗以\(k\)为根有 \(n\) 个节点的树,每个节点有一个点权\(v_i\)。有\(m\) 次操作
- 1 x y 表示将节点 \(x\)的权值加上 \(y\)
- 2 x 表示求以\(x\)为根的子树上所有节点(包括\(x\))的和
思路:
通过 dfs
序将一整棵子树上映射到序列中连续的一段上。问题就变成对数组进行:单点修改,区间查询。用树状数组维护就好了。
代码
/*
https://ac.nowcoder.com/acm/problem/204871
*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
const int maxn = 1e6 + 10;
int l[maxn], r[maxn];
int bits[maxn];
vector<int> G[maxn];
int n, m, k, cnt = 0;
int lowbit(int x){
return x & (-x);
}
void add(int x, int val){
while(x < maxn){
bits[x] += val;
x += lowbit(x);
}
}
int query(int x){
int res = 0;
while(x){
res += bits[x];
x -= lowbit(x);
}
return res;
}
void dfs(int u, int fa){
l[u] = ++cnt;
for(auto v : G[u]){
if(v == fa) continue;
dfs(v, u);
}
r[u] = cnt;
}
int a[maxn], op[maxn], b[maxn], val[maxn];
int main(){
cin >> n >> m >> k;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i < n; i++){
int u, v; cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
dfs(k, 0);
for(int i = 1; i <= n; i++) add(l[i], a[i]);
for(int i = 0; i < m; i++){
int op; cin >> op;
if(op == 1){
int x, y; cin >> x >> y;
add(l[x], y);
}
else{
int x; cin >> x;
cout << (query(r[x]) - query(l[x]-1)) << endl;
}
}
return 0;
}
Propagating tree 原CF383C
题意
一颗以\(1\)为根有 \(n\) 个节点的树,每个节点有一个点权\(a_i\)。有\(m\) 次操作
- 1 x y 表示将 \(x\) 结点权值 \(+val\) ,\(x\) 的儿子权值 \(-val\),\(x\) 的孙子们 \(+val\), 以此类推。
- 2 x 表示\(x\)的点权
思路
乍一看是树状数组差分,区间修改,单点查询。但是第一个操作搞不定,仔细观察后得到\(1\)操作和每个节点的深度奇偶有关。所以只需判断一下深度,决定修改的值的正负就可以(又没想到。。。)。
代码
/*
https://ac.nowcoder.com/acm/problem/110318
*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
const int maxn = 1e6 + 10;
int l[maxn], r[maxn];
int bits[maxn];
vector<int> G[maxn];
int n, m, k, cnt = 0;
int a[maxn], dep[maxn];
int lowbit(int x){
return x & (-x);
}
void add(int x, int val){
while(x < maxn){
bits[x] += val;
x += lowbit(x);
}
}
int query(int x){
int res = 0;
while(x){
res += bits[x];
x -= lowbit(x);
}
return res;
}
void dfs(int u, int fa){
l[u] = ++cnt;
dep[u] = dep[fa] + 1;
for(auto v : G[u]){
if(v == fa) continue;
dfs(v, u);
}
r[u] = cnt;
}
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i < n; i++){
int u, v; cin >> u >> v;
G[u].pb(v);
G[v].pb(u);
}
dfs(1, 0);
// for(int i = 1; i <= n; i++) add(l[i], a[i]);
for(int i = 0; i < m; i++){
int op; cin >> op;
if(op == 1){
int x, y; cin >> x >> y;
if(dep[x] & 1){
add(l[x], y);
add(r[x] + 1, -y);
} else {
add(l[x], -y);
add(r[x] + 1, y);
}
}
else{
int x; cin >> x;
int y = query(l[x]);
if(dep[x] & 1) cout << a[x] + y << endl;
else cout << a[x] - y << endl;
}
}
return 0;
}
[华华和月月种树]https://ac.nowcoder.com/acm/problem/23051)
题意
华华和月月一起维护了一棵动态有根树,每个点有一个权值。刚开存档的时候,树上只有 0 号节点,权值为 0 。接下来三种操作:
- \(1\) \(i\) 表示月月氪金使节点\(i\) 长出了一个新的儿子节点,权值为\(0\),编号为当前最大编号 \(+1\)(也可以理解为,当前是第几个操作 \(1\),新节点的编号就是多少)。
- \(2\) \(i\) \(a\) 表示华华上线做任务使节点 \(i\) 的子树中所有节点(即它和它的所有子孙节点)权值加 \(a\) 。
- \(3\) \(i\),华华需要给出 \(i\) 节点此时的权值。
思路
树状数组区间更新单点求值。
操作\(1\)可以将新加的点的减去它的父亲的权值来使得新加的点值为\(0\)。
代码
/*
https://ac.nowcoder.com/acm/problem/23051
https://blog.nowcoder.net/n/0055f19c0e49422786d7b7981a914709
*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pb push_back
const int maxn = 1e6 + 10;
int l[maxn], r[maxn];
int bits[maxn];
vector<int> G[maxn];
int m, cnt = 0;
int lowbit(int x){
return x & (-x);
}
void add(int x, int val){
while(x <= cnt + 1){
bits[x] += val;
x += lowbit(x);
}
}
int query(int x){
int res = 0;
while(x){
res += bits[x];
x -= lowbit(x);
}
return res;
}
void dfs(int u){
l[u] = ++cnt;
for(auto v : G[u]) dfs(v);
r[u] = cnt;
}
int a[maxn], op[maxn], b[maxn], val[maxn];
int main(){
cin >> m;
for(int i = 0; i < m; i++){
cin >> op[i] >> a[i];
if(op[i] == 2) cin >> b[i];
else if(op[i] == 1){
G[a[i]].pb(++cnt);
b[i] = cnt;
}
}
dfs(0);
// cout << cnt << endl;
for(int i = 0; i < m; i++){
if(op[i] == 1) val[l[b[i]]] -= query(l[a[i]]);
else if(op[i] == 2){
add(l[a[i]], b[i]);
add(r[a[i]] + 1, -b[i]);
}
else cout << val[l[a[i]]] + query(l[a[i]]) << endl;
}
return 0;
}
DFS序专题的更多相关文章
- Leetcode之深度优先搜索(DFS)专题-1080. 根到叶路径上的不足节点(Insufficient Nodes in Root to Leaf Paths)
Leetcode之深度优先搜索(DFS)专题-1080. 根到叶路径上的不足节点(Insufficient Nodes in Root to Leaf Paths) 这篇是DFS专题的第一篇,所以我会 ...
- BZOJ 3083: 遥远的国度 [树链剖分 DFS序 LCA]
3083: 遥远的国度 Time Limit: 10 Sec Memory Limit: 1280 MBSubmit: 3127 Solved: 795[Submit][Status][Discu ...
- BZOJ 4196: [Noi2015]软件包管理器 [树链剖分 DFS序]
4196: [Noi2015]软件包管理器 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 1352 Solved: 780[Submit][Stat ...
- BZOJ 2434: [Noi2011]阿狸的打字机 [AC自动机 Fail树 树状数组 DFS序]
2434: [Noi2011]阿狸的打字机 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 2545 Solved: 1419[Submit][Sta ...
- 【BZOJ-3779】重组病毒 LinkCutTree + 线段树 + DFS序
3779: 重组病毒 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 224 Solved: 95[Submit][Status][Discuss] ...
- 【BZOJ-1146】网络管理Network DFS序 + 带修主席树
1146: [CTSC2008]网络管理Network Time Limit: 50 Sec Memory Limit: 162 MBSubmit: 3495 Solved: 1032[Submi ...
- 【Codeforces163E】e-Government AC自动机fail树 + DFS序 + 树状数组
E. e-Government time limit per test:1 second memory limit per test:256 megabytes input:standard inpu ...
- 【BZOJ-3881】Divljak AC自动机fail树 + 树链剖分+ 树状数组 + DFS序
3881: [Coci2015]Divljak Time Limit: 20 Sec Memory Limit: 768 MBSubmit: 508 Solved: 158[Submit][Sta ...
- 2016 ACM/ICPC Asia Regional Dalian Online 1010 Weak Pair dfs序+分块
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)Total Submissio ...
随机推荐
- 数据恢复软件推荐-easyrecovery绿色破解版(附注册码)免费下载
easyrecovery破解版专注于PC端存储数据的抢救恢复,软件的整体界面风格和360杀毒有些许相似,没有看起来像牛皮藓的杂乱广告,只有六个功能按键,对应你所遇到的数据丢失状况级别,点击最为适合的功 ...
- Linux防火墙篇
关闭firewall:systemctl stop firewalld.service #停止firewallsystemctl disable firewalld.service #禁止f ...
- CodeForces 1344D Résumé Review
题意 给定一个长度为 \(n\) 的序列 \(a\) 和一个整数 \(k\),构造一个序列 \(b\) 使得满足以下条件: \(0\leq b_i\leq a_i\) \(\sum\limits_{i ...
- 云服务器部署Python项目(nginx+uwsgi+mysql+项目)
python项目部署到云服务器 关注公众号"轻松学编程"了解更多. 一.硬件准备 云服务器,系统ubuntu_16_04 . 注意:要在安全组中开放Http的80端口. 二.软件准 ...
- Python 列表的11个重要操作
列表是python中内置的数据结构,它的表现形式为方括号中不同数据的集合,用逗号分隔开.列表可以用来存储相同数据类型或不同数据类型. 列表是可变的,这也是它如此常用的原因,然而在某些情况下,可变性需要 ...
- 【SpringCloud】02.微服务与SpringCloud
微服务的特点 一系列微小的服务共同组成 跑在自己的进程里 每个服务为独立的业务开发 独立部署 分布式管理 异构--不同的语言.不同类型的数据库 微服务架构的基础框架/组件 服务注册发现 服务网关(Se ...
- 基于C++语言实现机动车违章处罚管理系统
这篇文章主要介绍了基于C++语言实现机动车违章处罚管理系统的相关资料,需要的朋友可以参考下 关键代码如下所示: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 ...
- 对List对象里的其中两个属性配套去重
List<User> disList= list.stream().collect(Collectors.collectingAndThen(Collectors.toCollection ...
- ubuntu下minicom安装和简单设置使用
minicom是一个串口通信工具. Ubuntu下的安装: 打开终端 sudo apt-get install minicom即可完成安装. 简单设置使用: 安装完成后第一次启动时运行 sudo mi ...
- Module not found: Can't resolve 'bootstrap/dist/css/bootstrap-theme.css' in 'C:\react-form-validation-demo\src'
此错误是由配置错误.版本不匹配或引导安装损坏引起的.如果已经安装了引导程序和反应引导程序,则可以通过以下方式进行更改: npm install --save bootstrap@^4.0.0-al ...