网上特别讲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序 参考许昊然《数据结构漫谈》的更多相关文章

  1. dfs序七个经典问题

    update-2018.07.23: 原文问题五思路描述有误,已更正. 参考自:<数据结构漫谈>-许昊然 dfs序是树在dfs先序遍历时的序列,将树形结构转化成序列问题处理. dfs有一个 ...

  2. 【转载】dfs序七个经典问题

    作者:weeping 出处:www.cnblogs.com/weeping/ 原文链接 https://www.cnblogs.com/weeping/p/6847112.html 参考自:<数 ...

  3. dfs序七个经典问题(转)

    我这个人不怎么喜欢写轻重链剖分和LCT 还是喜欢dfs序.括号序列之类的 毕竟线段树好写多了 然后就有了这篇转载的文章 写在这边以后有时间看看 原文链接:https://www.cnblogs.com ...

  4. dfs序七个经典问题[转]

    dfs序七个经典问题 参考自:<数据结构漫谈>-许昊然 dfs序是树在dfs先序遍历时的序列,将树形结构转化成序列问题处理. dfs有一个很好的性质:一棵子树所在的位置处于一个连续区间中. ...

  5. 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 ...

  6. CodeForces 570D DFS序 树状数组 Tree Requests

    参考九野巨巨的博客. 查询一个子树内的信息,可以通过DFS序转成线形的,从而用数据结构来维护. #include <iostream> #include <cstdio> #i ...

  7. BZOJ4034 [HAOI2015]树上操作+DFS序+线段树

    参考:https://www.cnblogs.com/liyinggang/p/5965981.html 题意:是一个数据结构题,树上的,用dfs序,变成线性的: 思路:对于每一个节点x,记录其DFS ...

  8. 【BZOJ-3779】重组病毒 LinkCutTree + 线段树 + DFS序

    3779: 重组病毒 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 224  Solved: 95[Submit][Status][Discuss] ...

  9. BZOJ_4867_[Ynoi2017]舌尖上的由乃_分块+dfs序

    BZOJ_4867_[Ynoi2017]舌尖上的由乃_分块+dfs序 Description 由乃为了吃到最传统最纯净的美食,决定亲自开垦一片菜园.现有一片空地,由乃已经规划n个地点准备种上蔬菜.最新 ...

随机推荐

  1. 国内流行的两大开源.net微信公众平台SDK对比分析

    最近忙于微信周边的开发 难免手痒去搜索一下有没有相关的sdk直接拿来使 还真发现了不少 这里总结两个看起来比较不错的.net平台下基于C#语言开发的SDK 一个强大一个小巧 (1) Senparc.W ...

  2. sql yog注册码

    Name: AnyRegistration Code: 26f359fc-e3f6-4727-8af1-72a1a4a0819d

  3. JavaScript HTML DOM - 改变 HTML

    JavaScript HTML DOM - 改变 HTML HTML DOM 允许 JavaScript 改变 HTML 元素的内容. 改变 HTML 输出流 JavaScript 能够创建动态的 H ...

  4. this的一个作用 当前对象

    class Person{ String name="小花"; int age=19; void eat(){  System.out.println("在吃饭" ...

  5. 谢尔排序/缩减增量排序(C++)

    谢尔排序/缩减增量排序(C++) 谢尔排序/缩减增量排序: 他通过比较相距一定间隔的元素来工作,各趟比较所用的距离随着算法的进行而减小,直到只比较相邻元素的最后一趟排序为止.(好复杂) 看了一下实现代 ...

  6. Codeforces 441D Valera and Swaps(置换群)

    题意: 给定一个1~n的排列(n<=3000),输出字典序最小且次数最少的交换操作,使得操作后的排列可以通过最少m次交换得到排列[1,2,...n] Solution: 可以将排列的对应关系看做 ...

  7. 【POJ2752】【KMP】Seek the Name, Seek the Fame

    Description The little cat is so famous, that many couples tramp over hill and dale to Byteland, and ...

  8. Linux redhat

    挂载U盘 fdisk -l 可以列出所有的分区,包括没有挂上的分区和usb设备.我一般用这个来查找需要挂载的分区的位置,比如挂上u盘. mount /dev/sdb1 usb/

  9. js 实现tab选项卡

    最近一直在研究js  如果不及时复习的话前边学到的东西很快就会忘掉,所以把前段时间的一个简单的tab选项卡的思路写出来也算复习了一下吧, 第一步:先把布局写出来: <div id="d ...

  10. 根据select不同的选项实现相应input框添加项的显示

    实现效果: @1.单击包时,显示包时的添加项 @2.单击包里程,显示包里程的添加项 二  代码实现: 给select添加change事件 获取当前select的value 根据value判断对象显示其 ...