DFS序 参考许昊然《数据结构漫谈》
网上特别讲DFS序的东西好像很少
太简单了? 实用性不大?
看了论文中 7个经典问题, 觉得挺有用的
原文
"所谓DFS序, 就是DFS整棵树依次访问到的结点组成的序列"
"DFS序有一个很强的性质: 一颗子树的所有节点在DFS序内是连续的一段, 利用这个性质我们可以解决很多问题"
基本代码 :
void Dfs(int u, int fa, int dep)
{
seq[++cnt] = u;
st[u] = cnt;
int len = edge[u].size();
for(int i = ; i < len; i++) {
int v = edge[u][i];
if(v != fa) {
Dfs(v, u, dep+);
}
}
seq[++cnt] = u;
ed[u] = cnt;
}
关于原文中列出的7个经典问题, 分别实现一下
给定一颗树, 和每个节点的权值
1. 对某个节点X权值加上一个数W, 查询某个子树X里所有点权的和
解 :
由于X子树在DFS序中是连续的一段, 只需要维护一个序列,
支持单点修改和区间查询, 用树状数组就能实现
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector> using namespace std; const int MAXN = 1e5+; vector<int> edge[MAXN];
int s[*MAXN];
int seq[*MAXN];
int st[MAXN];
int ed[MAXN];
int cnt; int Lowbit(int x)
{
return x & (-x);
} void Add(int x, int val, int n)
{
for(int i = x; i <= n; i += Lowbit(i)) {
s[i] += val;
}
} int Sum(int x)
{
int res = ;
for(int i = x; i > ; i -= Lowbit(i)) {
res += s[i];
}
return res;
} void Dfs(int u, int fa)
{
seq[++cnt] = u;
st[u] = cnt;
int len = edge[u].size();
for(int i = ; i < len; i++) {
int v = edge[u][i];
if(v != fa) {
Dfs(v, u);
}
}
seq[++cnt] = u;
ed[u] = cnt;
} void Init(int n)
{
for(int i = ; i <= n; i++) {
edge[i].clear();
}
memset(s, , sizeof(s));
} int main()
{
int n, op;
int u, v, w;
int cmd; while(scanf("%d %d", &n, &op) != EOF) {
Init(n);
for(int i = ; i < n-; i++) {
scanf("%d %d", &u, &v);
edge[u].push_back(v);
edge[v].push_back(u);
}
cnt = ;
Dfs(, -);
while(op--) {
scanf("%d", &cmd);
if(cmd == ) {
scanf("%d %d", &u, &w);
Add(st[u], w, cnt);
}
else if(cmd == ) {
scanf("%d", &u);
printf("%d\n", Sum(ed[u]) - Sum(st[u] - ));
}
}
} return ;
}
2. 对节点X到Y的最短路上所有点权都加一个数W, 查询某个点的权值
解 :
这个操作等价于
a. 对X到根节点路径上所有点权加W
b. 对Y到根节点路径上所有点权加W
c. 对LCA(x, y)到根节点路径上所有点权值减W
d. 对LCA(x,y)的父节点 parent(LCA(x, y))到根节点路径上所有权值减W
于是要进行四次这样从一个点到根节点的区间修改
将问题进一步简化, 进行一个点X到根节点的区间修改, 查询其他一点Y时
只有X在Y的子树内, X对Y的值才有贡献且贡献值为W
于是只需要更新四个点, 查询一个点的子树内所有点权的和即可
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector> using namespace std; const int MAXN = 1e5+; vector<int> edge[MAXN];
int s[*MAXN];
int seq[*MAXN];
int seq1[*MAXN];
int depth[*MAXN];
int first[MAXN];
int dp[*MAXN][];
int st[MAXN];
int ed[MAXN];
int parent[MAXN];
int cnt, num; int Lowbit(int x)
{
return x & (-x);
} void Add(int x, int val, int n)
{
if(x <= ) return;
for(int i = x; i <= n; i += Lowbit(i)) {
s[i] += val;
}
} int Sum(int x)
{
int res = ;
for(int i = x; i > ; i -= Lowbit(i)) {
res += s[i];
}
return res;
} void Dfs(int u, int fa, int dep)
{
parent[u] = fa;
seq[++cnt] = u;
seq1[++num] = u;
first[u] = num;
depth[num] = dep;
st[u] = cnt;
int len = edge[u].size();
for(int i = ; i < len; i++) {
int v = edge[u][i];
if(v != fa) {
Dfs(v, u, dep+);
seq1[++num] = u;
depth[num] = dep;
}
}
seq[++cnt] = u;
ed[u] = cnt;
} void RMQ_Init(int n)
{
for(int i = ; i <= n; i++) {
dp[i][] = i;
}
for(int j = ; ( << j) <= n; j++) {
for(int i = ; i + ( << j) - <= n; i++) {
int a = dp[i][j-], b = dp[i + ( << (j-))][j-];
dp[i][j] = depth[a] < depth[b] ? a : b;
}
}
} int RMQ_Query(int l, int r)
{
int k = ;
while(( << (k + )) <= r - l + ) k++;
int a = dp[l][k], b = dp[r-(<<k)+][k];
return depth[a] < depth[b] ? a : b;
} int LCA(int u, int v)
{
int a = first[u], b = first[v];
if(a > b) a ^= b, b ^= a, a ^= b;
int res = RMQ_Query(a, b);
return seq1[res];
} void Init(int n)
{
for(int i = ; i <= n; i++) {
edge[i].clear();
}
memset(s, , sizeof(s));
} int main()
{
int n, op;
int u, v, w;
int cmd; while(scanf("%d %d", &n, &op) != EOF) {
Init(n);
for(int i = ; i < n-; i++) {
scanf("%d %d", &u, &v);
edge[u].push_back(v);
edge[v].push_back(u);
}
cnt = , num = ;
Dfs(, -, );
RMQ_Init(num);
while(op--) {
scanf("%d", &cmd);
if(cmd == ) {
scanf("%d %d %d", &u, &v, &w);
int lca = LCA(u, v);
Add(st[u], w, cnt);
Add(st[v], w, cnt);
Add(lca, -w, cnt);
Add(parent[lca], -w, cnt);
}
else if(cmd == ) {
scanf("%d", &u);
printf("%d\n", Sum(ed[u]) - Sum(st[u] - ));
}
}
} return ;
}
3. 对节点X到Y的最短路上所有点权都加一个数W, 查询某个点子树的权值之和
解 :
同问题2中的修改方法, 转化为修改某点到根节点的权值加/减W
当修改某个节点A, 查询另一节点B时
只有A在B的子树内, Y的值会增加 W * (depth[A] - depth[B] + 1) == W * (depth[A] + 1) - W * depth[B]
同样是用树状数组来查询Y的子树, 修改 和 查询方法都要新增一个数组
第一个数组保存 W * (depth[A] + 1), 第二个数组保存 W
每次查询结果为Sum(ed[B]) - Sum(st[B]-1) - (Sum1(ed[B]) - Sum(st[B]-1)) * depth[B]
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector> using namespace std; const int MAXN = 1e5+; vector<int> edge[MAXN];
int s[*MAXN];
int s1[*MAXN];
int seq[*MAXN];
int seq1[*MAXN];
int depth[*MAXN];
int first[MAXN];
int dp[*MAXN][];
int st[MAXN];
int ed[MAXN];
int parent[MAXN];
int cnt, num; int Lowbit(int x)
{
return x & (-x);
} void Add(int x, int val, int n)
{
if(x <= ) return;
for(int i = x; i <= n; i += Lowbit(i)) {
s[i] += val;
}
} void Add1(int x, int val, int n)
{
if(x <= ) return;
for(int i = x; i <= n; i += Lowbit(i)) {
s1[i] += val;
}
} int Sum(int x)
{
int res = ;
for(int i = x; i > ; i -= Lowbit(i)) {
res += s[i];
}
return res;
} int Sum1(int x)
{
int res = ;
for(int i = x; i > ; i -= Lowbit(i)) {
res += s1[i];
}
return res;
} void Dfs(int u, int fa, int dep)
{
parent[u] = fa;
seq[++cnt] = u;
seq1[++num] = u;
first[u] = num;
depth[num] = dep;
st[u] = cnt;
int len = edge[u].size();
for(int i = ; i < len; i++) {
int v = edge[u][i];
if(v != fa) {
Dfs(v, u, dep+);
seq1[++num] = u;
depth[num] = dep;
}
}
seq[++cnt] = u;
ed[u] = cnt;
} void RMQ_Init(int n)
{
for(int i = ; i <= n; i++) {
dp[i][] = i;
}
for(int j = ; ( << j) <= n; j++) {
for(int i = ; i + ( << j) - <= n; i++) {
int a = dp[i][j-], b = dp[i + ( << (j-))][j-];
dp[i][j] = depth[a] < depth[b] ? a : b;
}
}
} int RMQ_Query(int l, int r)
{
int k = ;
while(( << (k + )) <= r - l + ) k++;
int a = dp[l][k], b = dp[r-(<<k)+][k];
return depth[a] < depth[b] ? a : b;
} int LCA(int u, int v)
{
int a = first[u], b = first[v];
if(a > b) a ^= b, b ^= a, a ^= b;
int res = RMQ_Query(a, b);
return seq1[res];
} void Init(int n)
{
for(int i = ; i <= n; i++) {
edge[i].clear();
}
memset(s, , sizeof(s));
} int main()
{
int n, op;
int u, v, w;
int cmd; while(scanf("%d %d", &n, &op) != EOF) {
Init(n);
for(int i = ; i < n-; i++) {
scanf("%d %d", &u, &v);
edge[u].push_back(v);
edge[v].push_back(u);
}
cnt = , num = ;
Dfs(, -, );
RMQ_Init(num);
while(op--) {
scanf("%d", &cmd);
if(cmd == ) {
scanf("%d %d %d", &u, &v, &w);
int lca = LCA(u, v);
Add(st[u], w * depth[first[u]] + w, cnt);
Add1(st[u], w, cnt);
Add(st[v], w * depth[first[v]] + w, cnt);
Add1(st[v], w, cnt);
Add(lca, -(w * depth[first[lca]] + w), cnt);
Add1(lca, -w, cnt);
Add(parent[lca], -(w * depth[first[parent[lca]]] + w), cnt);
Add1(parent[lca], -w, cnt);
}
else if(cmd == ) {
scanf("%d", &u);
printf("%d\n", Sum(ed[u]) - Sum(st[u] - ) - \
depth[first[u]] * (Sum1(ed[u]) - Sum1(st[u] - )));
}
}
} return ;
}
4. 对某个点X权值加上一个数W, 查询X到Y路径上所有点权之和
解 :
求X到Y路径上所有的点权之和, 和前面X到Y路径上所有点权加一个数相似
这个问题转化为
X到根节点的和 + Y到根节点的和 - LCA(x, y)到根节点的和 - parent(LCA(x,y)) 到根节点的和
于是只要支持单点修改, 区间查询即可
要注意的是修改, 要在该点开始出现位置加W, 在结束位置减W
这样能保证在该节点子树内的能加到这个W, 不在该点子树内的无影响
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector> using namespace std; const int MAXN = 1e5+; vector<int> edge[MAXN];
int s[*MAXN];
int s1[*MAXN];
int seq[*MAXN];
int seq1[*MAXN];
int depth[*MAXN];
int first[MAXN];
int dp[*MAXN][];
int st[MAXN];
int ed[MAXN];
int parent[MAXN];
int cnt, num; int Lowbit(int x)
{
return x & (-x);
} void Add(int x, int val, int n)
{
if(x <= ) return;
for(int i = x; i <= n; i += Lowbit(i)) {
s[i] += val;
}
} int Sum(int x)
{
int res = ;
for(int i = x; i > ; i -= Lowbit(i)) {
res += s[i];
}
return res;
} void Dfs(int u, int fa, int dep)
{
parent[u] = fa;
seq[++cnt] = u;
seq1[++num] = u;
first[u] = num;
depth[num] = dep;
st[u] = cnt;
int len = edge[u].size();
for(int i = ; i < len; i++) {
int v = edge[u][i];
if(v != fa) {
Dfs(v, u, dep+);
seq1[++num] = u;
depth[num] = dep;
}
}
seq[++cnt] = u;
ed[u] = cnt;
} void RMQ_Init(int n)
{
for(int i = ; i <= n; i++) {
dp[i][] = i;
}
for(int j = ; ( << j) <= n; j++) {
for(int i = ; i + ( << j) - <= n; i++) {
int a = dp[i][j-], b = dp[i + ( << (j-))][j-];
dp[i][j] = depth[a] < depth[b] ? a : b;
}
}
} int RMQ_Query(int l, int r)
{
int k = ;
while(( << (k + )) <= r - l + ) k++;
int a = dp[l][k], b = dp[r-(<<k)+][k];
return depth[a] < depth[b] ? a : b;
} int LCA(int u, int v)
{
int a = first[u], b = first[v];
if(a > b) a ^= b, b ^= a, a ^= b;
int res = RMQ_Query(a, b);
return seq1[res];
} void Init(int n)
{
for(int i = ; i <= n; i++) {
edge[i].clear();
}
memset(s, , sizeof(s));
} int main()
{
int n, op;
int u, v, w;
int cmd; while(scanf("%d %d", &n, &op) != EOF) {
Init(n);
for(int i = ; i < n-; i++) {
scanf("%d %d", &u, &v);
edge[u].push_back(v);
edge[v].push_back(u);
}
cnt = , num = ;
Dfs(, -, );
RMQ_Init(num);
while(op--) {
scanf("%d", &cmd);
if(cmd == ) {
scanf("%d %d", &u, &w);
Add(st[u], w, cnt);
Add(ed[u], -w, cnt);
}
else if(cmd == ) {
scanf("%d %d", &u, &v);
int lca = LCA(u, v);
printf("%d\n", Sum(st[u]) + Sum(st[v]) - Sum(st[lca]) - Sum(st[parent[lca]]));
}
}
} return ;
}
5. 对子树X里所有节点加上一个值W, 查询某个点的值
解 :
对DFS序来说, 子树内所有节点加W, 就是一段区间加W
所以这个问题就是 区间修改, 单点查询
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector> using namespace std; const int MAXN = 1e5+; vector<int> edge[MAXN];
int s[*MAXN];
int s1[*MAXN];
int seq[*MAXN];
int seq1[*MAXN];
int depth[*MAXN];
int first[MAXN];
int dp[*MAXN][];
int st[MAXN];
int ed[MAXN];
int parent[MAXN];
int cnt, num; int Lowbit(int x)
{
return x & (-x);
} void Add(int x, int val, int n)
{
if(x <= ) return;
for(int i = x; i <= n; i += Lowbit(i)) {
s[i] += val;
}
} int Sum(int x)
{
int res = ;
for(int i = x; i > ; i -= Lowbit(i)) {
res += s[i];
}
return res;
} void Dfs(int u, int fa)
{
seq[++cnt] = u;
st[u] = cnt;
int len = edge[u].size();
for(int i = ; i < len; i++) {
int v = edge[u][i];
if(v != fa) {
Dfs(v, u);
}
}
seq[++cnt] = u;
ed[u] = cnt;
} void Init(int n)
{
for(int i = ; i <= n; i++) {
edge[i].clear();
}
memset(s, , sizeof(s));
} int main()
{
int n, op;
int u, v, w;
int cmd; while(scanf("%d %d", &n, &op) != EOF) {
Init(n);
for(int i = ; i < n-; i++) {
scanf("%d %d", &u, &v);
edge[u].push_back(v);
edge[v].push_back(u);
}
cnt = ;
Dfs(, -);
while(op--) {
scanf("%d", &cmd);
if(cmd == ) {
scanf("%d %d", &u, &w);
Add(st[u], w, cnt);
Add(ed[u], -w, cnt);
}
else if(cmd == ) {
scanf("%d", &u);
printf("%d\n", Sum(u));
}
}
} return ;
}
6.对子树X里所有节点加上一个值W, 查询某个子树的权值和
解 :
子树所有节点加W, 就是某段区间加W, 查询某个子树的权值和, 就是查询某段区间的和
区间修改区间求和, 是线段树的经典问题, 用线段树可以很好解决
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector> using namespace std; typedef struct {
int l, r, sum, add;
} Seg; const int MAXN = 1e5+; Seg T[*MAXN];
vector<int> edge[MAXN];
int s[*MAXN];
int seq[*MAXN];
int st[MAXN];
int ed[MAXN];
int cnt; void Dfs(int u, int fa)
{
seq[++cnt] = u;
st[u] = cnt;
int len = edge[u].size();
for(int i = ; i < len; i++) {
int v = edge[u][i];
if(v != fa) {
Dfs(v, u);
}
}
seq[++cnt] = u;
ed[u] = cnt;
} void Build(int l, int r, int k)
{
T[k].l = l, T[k].r = r, T[k].sum = T[k].add;
if(l == r) return;
int mid = (l + r) >> ;
Build(l, mid, k<<);
Build(mid+, r, k<<|);
} void PushDown(int k)
{
if(T[k].add) {
T[k<<].sum += (T[k<<].r - T[k<<].l + ) * T[k].add;
T[k<<].add += T[k].add;
T[k<<|].sum += (T[k<<|].r - T[k<<|].l + ) * T[k].add;
T[k<<|].add += T[k<<].add;
T[k].add = ;
}
} void PushUp(int k)
{
T[k].sum = T[k<<].sum + T[k<<|].sum;
} void Update(int l, int r, int val, int k)
{
if(T[k].l == l && T[k].r == r) {
T[k].sum += (r - l + ) * val;
T[k].add += val;
return ;
}
PushDown(k);
int mid = (T[k].l + T[k].r) >> ;
if(r <= mid) Update(l, r, val, k<<);
else if(l > mid) Update(l, r, val, k<<|);
else {
Update(l, mid, val, k<<);
Update(mid+, r, val, k<<|);
}
PushUp(k);
} int Query(int l, int r, int k)
{
if(T[k].l == l && T[k].r == r) {
return T[k].sum;
}
PushDown(k);
int mid = (T[k].l + T[k].r) >> ;
if(r <= mid) return Query(l, r, k<<);
else if(l > mid) return Query(l, r, k<<|);
else {
return Query(l, mid, k<<) + Query(mid+, r, k<<|);
}
} void Init(int n)
{
for(int i = ; i <= n; i++) {
edge[i].clear();
}
} int main()
{
int n, op;
int u, v, w;
int cmd; while(scanf("%d %d", &n, &op) != EOF) {
Init(n);
for(int i = ; i < n-; i++) {
scanf("%d %d", &u, &v);
edge[u].push_back(v);
edge[v].push_back(u);
}
cnt = ;
Dfs(, -);
Build(, cnt, );
while(op--) {
scanf("%d", &cmd);
if(cmd == ) {
scanf("%d %d", &u, &w);
Update(st[u], ed[u], w, );
}
else if(cmd == ) {
scanf("%d", &u);
printf("%d\n", Query(st[u], ed[u], ) / );
}
}
} return ;
}
7. 对节点X的子树所有节点加上一个值W, 查询X到Y的路径上所有点的权值和
解:
同问题4把路径上求和转化为四个点到根节点的和
X到根节点的和 + Y到根节点的和 - LCA(x, y)到根节点的和 - parent(LCA(x,y)) 到根节点的和
修改一点A, 查询某点B到根节点时, 只有B在A的子树内, A对B才有贡献
贡献为W * (depth[B] - depth[A] + 1) == W * (1 - depth[A]) + W * depth[B]
和第三题一样, 用两个树状数组维护 W *(depth[A] + 1), W * depth[B]
同样要注意修改某点时, 在一点开始位置加, 在其结束位置减
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector> using namespace std; typedef struct {
int l, r, sum, add;
} Seg; const int MAXN = 1e5+; Seg T[*MAXN];
vector<int> edge[MAXN];
int s[*MAXN];
int s1[*MAXN];
int seq[*MAXN];
int seq1[*MAXN];
int depth[*MAXN];
int first[MAXN];
int dp[*MAXN][];
int parent[MAXN];
int st[MAXN];
int ed[MAXN];
int cnt, cnt1; int Lowbit(int x)
{
return x & (-x);
} void Add(int x, int val, int n)
{
if(x <= ) return ;
for(int i = x; i <= n; i += Lowbit(i)) {
s[i] += val;
}
} void Add1(int x, int val, int n)
{
if(x <= ) return ;
for(int i = x; i <= n; i += Lowbit(i)) {
s1[i] += val;
}
} int Sum(int x)
{
int res = ;
for(int i = x; i > ; i -= Lowbit(i)) {
res += s[i];
}
return res;
} int Sum1(int x)
{
int res = ;
for(int i = x; i > ; i -= Lowbit(i)) {
res += s1[i];
}
return res;
} void RMQ_Init(int n)
{
for(int i = ; i <= n; i++) {
dp[i][] = i;
}
for(int j = ; ( << j) <= n; j++) {
for(int i = ; i + ( << j) - <= n; i++) {
int a = dp[i][j-], b = dp[i + ( << (j-))][j-];
dp[i][j] = depth[a] < depth[b] ? a : b;
}
}
} int RMQ_Query(int l, int r)
{
int k = ;
while(( << (k + )) <= r - l + ) k++;
int a = dp[l][k], b = dp[r-( << k)+][k];
return depth[a] < depth[b] ? a : b;
} int LCA(int u, int v)
{
int a = first[u], b = first[v];
if(a > b) a ^= b, b ^= a, a ^= b;
int res = RMQ_Query(a, b);
return seq1[res];
} void Dfs(int u, int fa, int dep)
{
seq[++cnt] = u;
seq1[++cnt1] = u;
first[u] = cnt1;
parent[u] = fa;
depth[cnt1] = dep;
st[u] = cnt;
int len = edge[u].size();
for(int i = ; i < len; i++) {
int v = edge[u][i];
if(v != fa) {
Dfs(v, u, dep+);
seq1[++cnt1] = u;
depth[cnt1] = dep;
}
}
seq[++cnt] = u;
ed[u] = cnt;
} void Init(int n)
{
for(int i = ; i <= n; i++) {
edge[i].clear();
}
memset(s, , sizeof(s));
memset(s1, , sizeof(s1));
} void Debug()
{
int u, v;
while() {
scanf("%d %d", &u, &v);
printf("The LCA of %d %d is %d\n", u, v, LCA(u, v));
}
} int main()
{
int n, op;
int u, v, w;
int cmd; while(scanf("%d %d", &n, &op) != EOF) {
Init(n);
for(int i = ; i < n-; i++) {
scanf("%d %d", &u, &v);
edge[u].push_back(v);
edge[v].push_back(u);
}
cnt = cnt1 = ;
Dfs(, , );
RMQ_Init(cnt1);
while(op--) {
scanf("%d", &cmd);
if(cmd == ) {
scanf("%d %d", &u, &w);
Add(st[u], w * ( - depth[first[u]]), cnt);
Add(ed[u], -w * ( - depth[first[u]]), cnt);
Add1(st[u], w, cnt);
Add1(ed[u], -w, cnt);
}
else if(cmd == ) {
scanf("%d %d", &u, &v);
int lca = LCA(u, v);
int par = parent[lca];
int ans = Sum(st[u]);
ans += depth[first[u]] * Sum1(st[u]);
ans += Sum(st[v]);
ans += depth[first[v]] * Sum1(st[v]);
ans -= Sum(st[lca]);
ans -= depth[first[lca]] * Sum1(st[lca]);
ans -= Sum(st[par]);
ans -= depth[first[par]] * Sum1(st[par]);
printf("%d\n", ans);
}
}
} return ;
}
DFS序 参考许昊然《数据结构漫谈》的更多相关文章
- dfs序七个经典问题
update-2018.07.23: 原文问题五思路描述有误,已更正. 参考自:<数据结构漫谈>-许昊然 dfs序是树在dfs先序遍历时的序列,将树形结构转化成序列问题处理. dfs有一个 ...
- 【转载】dfs序七个经典问题
作者:weeping 出处:www.cnblogs.com/weeping/ 原文链接 https://www.cnblogs.com/weeping/p/6847112.html 参考自:<数 ...
- dfs序七个经典问题(转)
我这个人不怎么喜欢写轻重链剖分和LCT 还是喜欢dfs序.括号序列之类的 毕竟线段树好写多了 然后就有了这篇转载的文章 写在这边以后有时间看看 原文链接:https://www.cnblogs.com ...
- dfs序七个经典问题[转]
dfs序七个经典问题 参考自:<数据结构漫谈>-许昊然 dfs序是树在dfs先序遍历时的序列,将树形结构转化成序列问题处理. dfs有一个很好的性质:一棵子树所在的位置处于一个连续区间中. ...
- SPOJ Query on a tree III (树剖(dfs序)+主席树 || Splay等平衡树)(询问点)
You are given a node-labeled rooted tree with n nodes. Define the query (x, k): Find the node whose ...
- CodeForces 570D DFS序 树状数组 Tree Requests
参考九野巨巨的博客. 查询一个子树内的信息,可以通过DFS序转成线形的,从而用数据结构来维护. #include <iostream> #include <cstdio> #i ...
- BZOJ4034 [HAOI2015]树上操作+DFS序+线段树
参考:https://www.cnblogs.com/liyinggang/p/5965981.html 题意:是一个数据结构题,树上的,用dfs序,变成线性的: 思路:对于每一个节点x,记录其DFS ...
- 【BZOJ-3779】重组病毒 LinkCutTree + 线段树 + DFS序
3779: 重组病毒 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 224 Solved: 95[Submit][Status][Discuss] ...
- BZOJ_4867_[Ynoi2017]舌尖上的由乃_分块+dfs序
BZOJ_4867_[Ynoi2017]舌尖上的由乃_分块+dfs序 Description 由乃为了吃到最传统最纯净的美食,决定亲自开垦一片菜园.现有一片空地,由乃已经规划n个地点准备种上蔬菜.最新 ...
随机推荐
- 函数对象的prototype总结
通过看 http://www.cnblogs.com/mindsbook/archive/2009/09/19/javascriptYouMustKnowPrototype.html 该文章和对代码的 ...
- WordPress4.1新的函数介绍
wordpress 4.1也更新了一阵子了,作为一般用户最关系的就是新的wordpress主题,作为开发者,新增的函数也给我们带来了更多的便捷和惊喜,今天就转载一篇介绍wordpress4.1中新增的 ...
- html 5的localstorag
随着我们硬件技术的发展,浏览器本身的功能也愈发的完善,从之前的cookie到现在的本地缓存机制,再到web storage,在之前html4 的时候使用cookie具有一些明显的局限,如大小限制,co ...
- 下拉框——把一个select框中选中内容移到另一个select框中遇到的问题
在使用jQuery实现把一个select框中选中内容移到另一个select框中功能时遇到了一个问题,就是点击按钮时内容可以到另一个select框中,但是到了另一个select框中的内容却很快闪退回原来 ...
- 同步异步GET和POST请求
1.同步请求可以从因特网请求数据,一旦发送同步请求,程序将停止用户交互,直至服务器返回数据完成,才可以进行下一步操作, 2.异步请求不会阻塞主线程,而会建立一个新的线程来操作,用户发出异步请求后,依然 ...
- go-nsq使用简述
一 环境依赖: golang 开发环境(version >= 1.2) 下源码,配置环境变量,执行安装脚本 gpm 依赖包管理器 ...
- 谈谈Parser --王垠
一直很了解人们对于parser的误解,可是一直都提不起兴趣来阐述对它的观点.然而我觉得是有必要解释一下这个问题的时候了.我感觉得到大部分人对于parser的误解之深,再不澄清一下,恐怕这些谬误就要写进 ...
- BZOJ 1007 水平可见直线
Description 在xoy直角坐标平面上有n条直线L1,L2,...Ln,若在y值为正无穷大处往下看,能见到Li的某个子线段,则称Li为可见的,否则Li为被覆盖的. 例如,对于直线: ...
- BZOJ 1507 Editor
Description Input 输入文件editor.in的第一行是指令条数t,以下是需要执行的t个操作.其中: 为了使输入文件便于阅读,Insert操作的字符串中可能会插入一些回车符,请忽略掉它 ...
- How Much Did It Rain? Winner's Interview: 1st place, Devin Anzelmo
How Much Did It Rain? Winner's Interview: 1st place, Devin Anzelmo An early insight into the importa ...