dfs序学习总结
dfs序:
每个节点在dfs中的进出栈的时间序列。
树是非线性结构,根节点连着子节点,那么dfs序...节点进出栈的时间先后?
从根节点入栈,然后左儿子入栈,左儿子出栈,右儿子入栈,右儿子出栈,根节点出栈。
某节点的儿子在序列中的位置大于该节点入栈的位置小于该节点出栈的位置
这样就将一棵树变成了一个区间问题
(从某个节点入栈到某个节点出栈之间的节点都是该节点的子树??)
例:
1 2 3 3 2 1
1的左儿子为2,2的左儿子为3
1 2 2 3 3 1
1的左儿子为2,右儿子为3
(图片来源于网络)
比如这棵树,我们用dfs来搞一遍
那么这个树的dfs序就是
其中每个节点都出现了两次,一次是进入dfs的时刻,第二次是离开dfs的时刻,分别称之为In与Out。在区间操作中,如果某个节点出现了2次,则该节点将被“抵消”。所以通常会将Out时刻对应的点设置为负数。
性质:
现在来介绍dfs序列一些有用的性质:
任意子树都是连续的。例如假设有个子树BEFK,在序列中对应的部分是:BEEFKKFB;子树CGHI,在序列中对应的部分是:CGGHHIIC。
任意点对(a,b)之间的路径,可分为两种情况,首先是令lca是a、b的最近公共祖先:
1.若lca是a、b之一,则a、b之间的in时刻的区间或者out时刻区间就是其路径。例如AK之间的路径就对应区间ABEEFK或者KFBCGGHHIICA。
2.若lca另有其人,则a、b之间的路径为In[a]、Out[b]之间的区间或者In[b]、Out[a]之间的区间。另外,还需额外加上lca!!!考虑EK路径,对应为EFK再加上B。考虑EH之间的路径,对应为EFKKFBCGGH再加上A。
利用这些性质,可以利用DFS序列完成子树操作和路径操作。据传说还有更强大的使用方法,然而我还不会就是
#include<bits/stdc++.h>
#define maxn 100076
using namespace std;
struct treedfs{
int y, next;
}e[maxn];
int lin[maxn];
int tree_dfs[maxn], top = 0;
int in[maxn], out[maxn];
int n, len = 0;
bool flag[maxn];
inline void add (int x, int y){
e[++len].y = y;
e[len].next = lin[x];
lin[x] = len;
}
void dfs (int c) {
tree_dfs[++top] = c;
in[c] = top;
flag[c] = 1;
for (int i = lin[c]; i ;i = e[i].next) {
int to = e[i].y;
if(!flag[to])
dfs(to);
}
tree_dfs[++top] = c;
out[c] = top;
}
int main() {
memset(flag, 0, sizeof(flag));
scanf("%d", &n);
for (int i = 1;i <= n-1; i++) {
int x, y;
scanf("%d%d", &x, &y);
add(x, y);
add(y, x);
}
dfs(1);
for(int i = 1; i <= top; i++)
cout << tree_dfs[i] << ' ';
cout << endl;
for(int i = 1; i <= n; i++)
cout << in[i] << ' ' << out[i] << endl;
return 0;
}
Emmmm这大概就是dfs序的基础代码,输出一棵树的dfs序。
哦天哪,看看这丑陋的代码(捂脸)……
事实上我们也完全可以只记录dfs过程中被搜索到的顺序的先后。
void dfs(int c){
id[++top] = c;
in[c] = top;
f[c] = 1;
for(int i = lin[c]; i ; i = e[i].next){
int to = e[i].y;
if(!f[to])
dfs(to);
}
out[c] = top;
}
在这里id数组记录的就是这个树实际进行dfs的搜索顺序的先后
注:这里in和out记录的位置是不同的
这里in记录的确实按照只记录每个节点的被搜索的先后的方式记录的,out记录的则是以in中记录的那个节点的所有子树的终止位置,这么说可能不太清楚,举个例子
一棵树的关系是这样的:每行为x和y,表示x是y的父亲
1 4
5 4
1 3
2 4
对于这么一棵树,按照以上代码得到的dfs序为:
1 3 4 2 5
而在in和out中记录的分别是:
1 5
2 2
3 5
4 4
5 5
也就是说:
1是根节点,所以1的子树有子树就有3和4 2 5,out中1的结束下标就是5
3没有子树,他在out中的记录的位置和在in中是一样的
以此类推。
那么这个操作有什么用呢?emmmm我也不知道有什么用。
反正他应该和我们最初讨论的是一样的,都是把树变成区间然后用线段树进行操作
拿道具体题看看
Jzoj的艰难的回寝之路,原题是usaco的慢下来。
思路非常明显,dfs序+线段树就可以简单a掉了
代码:
#include<bits/stdc++.h>
#define maxn 500086
using namespace std;
struct node{
int y, next;
}e[maxn];
int tree[maxn];
int lin[maxn];
int n, len = 0, top = 0;
int in[maxn], out[maxn], id[maxn];
bool f[maxn];
inline int read(){
int x = 0;
char ch = getchar();
while(ch < '0' || ch > '9')
ch = getchar();
while(ch >= '0' && ch <= '9'){
x = x * 10 + ch - '0';
ch = getchar();
}
return x;
}
inline void add(int x, int y){
e[++len].y = y;
e[len].next = lin[x];
lin[x] = len;
}
void dfs(int c){
id[++top] = c;
in[c] = top;
f[c] = 1;
for(int i = lin[c]; i ; i = e[i].next){
int to = e[i].y;
if(!f[to])
dfs(to);
}
out[c] = top;
}
inline void pushdown(int pos){
if(!tree[pos]) return;
int lc = pos << 1, rc = pos << 1 | 1, v = tree[pos];
tree[lc] += v;
tree[rc] += v;
tree[pos] = 0;
}
void update(int pos,int L,int R,int l,int r){
if(l > R || r < L) return;
if(l >= L && r <= R){
tree[pos]++;
return ;
}
pushdown(pos);
int m= l + r >> 1;
update(pos << 1, L, R, l, m);
update(pos << 1 | 1, L, R, m+1, r);
}
int query(int pos,int aim,int l,int r){
if(l == r)return tree[pos];
pushdown(pos);
int m = l + r >> 1;
if(aim <= m) return query(pos << 1, aim, l, m);
else return query(pos << 1 | 1, aim, m+1, r);
}
int main(){
memset(f, 0, sizeof(f));
cin>>n;
for(int i=1;i<=n-1;i++){
int x, y;
x = read(); y = read();
add(x, y);
add(y, x);
}
dfs(1);
for(int i = 1; i <= n; i++){
int k;
k = read();
cout<<query(1, in[k], 1, n)<<endl;
update(1, in[k] + 1, out[k], 1, n);
}
return 0;
}
dfs序学习总结的更多相关文章
- Educational Codeforces Round 6 E dfs序+线段树
题意:给出一颗有根树的构造和一开始每个点的颜色 有两种操作 1 : 给定点的子树群体涂色 2 : 求给定点的子树中有多少种颜色 比较容易想到dfs序+线段树去做 dfs序是很久以前看的bilibili ...
- 【SPOJ】10628. Count on a tree(lca+主席树+dfs序)
http://www.spoj.com/problems/COT/ (速度很快,排到了rank6) 这题让我明白了人生T_T 我知道我为什么那么sb了. 调试一早上都在想人生. 唉. 太弱. 太弱. ...
- 【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)
http://www.lydsy.com/JudgeOnline/problem.php?id=1146 第一种做法(时间太感人): 第二种做法(rank5,好开心) ================ ...
- dfs序和欧拉序
生命不息,学习不止,昨天学了两个算法,总结一下,然而只是略懂,请路过的大佬多多谅解. 一.dfs序 1.什么是dfs序? 其实完全可以从字面意义上理解,dfs序就是指一棵树被dfs时所经过的节点的 ...
- BZOJ4551[Tjoi2016&Heoi2016]树——dfs序+线段树/树链剖分+线段树
题目描述 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为1),有以下 两种操作:1. 标记操作:对某个结点打上标记(在最开始,只有结点1有标记,其他结点均 ...
- 【AC自动机】【树状数组】【dfs序】洛谷 P2414 [NOI2011]阿狸的打字机 题解
这一题是对AC自动机的充分理解和树dfs序的巧妙运用. 题目背景 阿狸喜欢收藏各种稀奇古怪的东西,最近他淘到一台老式的打字机. 题目描述 打字机上只有28个按键,分别印有26个小写英文字母和' ...
- Codeforces - 570D 离散DFS序 特殊的子树统计 (暴力出奇迹)
题意:给定一棵树,树上每个节点有对应的字符,多次询问在\(u\)子树的深度为\(d\)的所有节点上的字符任意组合能否凑成一个回文串 把dfs序存储在一个二维线性表中,一个维度记录字符另一个维度记录深度 ...
- 浅谈DFS序
浅谈DFS序 本篇随笔简要讲解一下信息学奥林匹克竞赛中有关树的DFS序的相关内容. DFS序的概念 先来上张图: 树的DFS序,简单来讲就是对树从根开始进行深搜,按搜到的时间顺序把所有节点打上时间戳. ...
- dfs序+RMQ求LCA详解
首先安利自己倍增求LCA的博客,前置(算不上)知识在此. LCA有3种求法:倍增求lca(上面qwq),树链剖分求lca(什么时候会了树链剖分再说.),还有,标题. 是的你也来和我一起学习这个了qwq ...
随机推荐
- WIndows 相关知识
Windows服务 图解WinXP局域网共享设置步骤 Win10如何搭建FTP服务器以实现快速传输文件 强大工具psexec工具用法简介 BIOS和CMOS的区别 系统CLSID简介和小技巧
- [Leetcode] first missing positve 缺失的第一个正数
Given an unsorted integer array, find the first missing positive integer. For example,Given[1,2,0]re ...
- BZOJ 2707: [SDOI2012]走迷宫 拓扑+高斯消元+期望概率dp+Tarjan
先Tarjan缩点 强连通分量里用高斯消元外面直接转移 注意删掉终点出边和拓扑 #include<cstdio> #include<cstring> #include<a ...
- chrome 不支持12px以下字体为题的解决
现英文9px 设置 在chrome 下无效,可以通过 -webkit-transform: scale(0.75); 12*0.75 =9 得到小字体(在chrome浏览器下 大小缩放到0.75倍) ...
- HDU1166 敌兵布阵(树状数组实现
敌兵布阵 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submis ...
- POJ 1050 To the Max 二维最大子段和
To the MaxTime Limit: 1000MS Memory Limit: 10000KTotal Submissions: 52281 Accepted: 27633Description ...
- Java之戳中痛点 - (1)易变业务使用脚本语言编写
脚本语言的3大特征: 1.灵活:脚本语言一般是动态类型,可以不声明变量类型直接使用,也可以在运行期改变类型:2.便捷:脚本语言是解释性语言,在运行期变更非常方便,而不用重启服务3.简单:脚本语言语法比 ...
- VC关于置顶窗口的方法小结
转摘自:http://blog.csdn.net/wirror800/article/details/4002381 将窗体置顶的方法有: //将窗体置顶的API函数 ::SetWindowPos(m ...
- 数据结构之DFS与BFS
深度搜索(DFS) and 广度搜索(BFS) 代码如下: #include "stdafx.h" #include<iostream> #include<st ...
- 知问前端——日历UI(二)
datapicker外观选项 属性 默认值/类型 说明 disabled false/布尔值 禁用日历 numberOfMonths 1/数值 日历中同时显示的月份个数.默认为1,如果设置3就同时显示 ...