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个地点准备种上蔬菜.最新 ...
随机推荐
- 国内流行的两大开源.net微信公众平台SDK对比分析
最近忙于微信周边的开发 难免手痒去搜索一下有没有相关的sdk直接拿来使 还真发现了不少 这里总结两个看起来比较不错的.net平台下基于C#语言开发的SDK 一个强大一个小巧 (1) Senparc.W ...
- sql yog注册码
Name: AnyRegistration Code: 26f359fc-e3f6-4727-8af1-72a1a4a0819d
- JavaScript HTML DOM - 改变 HTML
JavaScript HTML DOM - 改变 HTML HTML DOM 允许 JavaScript 改变 HTML 元素的内容. 改变 HTML 输出流 JavaScript 能够创建动态的 H ...
- this的一个作用 当前对象
class Person{ String name="小花"; int age=19; void eat(){ System.out.println("在吃饭" ...
- 谢尔排序/缩减增量排序(C++)
谢尔排序/缩减增量排序(C++) 谢尔排序/缩减增量排序: 他通过比较相距一定间隔的元素来工作,各趟比较所用的距离随着算法的进行而减小,直到只比较相邻元素的最后一趟排序为止.(好复杂) 看了一下实现代 ...
- Codeforces 441D Valera and Swaps(置换群)
题意: 给定一个1~n的排列(n<=3000),输出字典序最小且次数最少的交换操作,使得操作后的排列可以通过最少m次交换得到排列[1,2,...n] Solution: 可以将排列的对应关系看做 ...
- 【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 ...
- Linux redhat
挂载U盘 fdisk -l 可以列出所有的分区,包括没有挂上的分区和usb设备.我一般用这个来查找需要挂载的分区的位置,比如挂上u盘. mount /dev/sdb1 usb/
- js 实现tab选项卡
最近一直在研究js 如果不及时复习的话前边学到的东西很快就会忘掉,所以把前段时间的一个简单的tab选项卡的思路写出来也算复习了一下吧, 第一步:先把布局写出来: <div id="d ...
- 根据select不同的选项实现相应input框添加项的显示
实现效果: @1.单击包时,显示包时的添加项 @2.单击包里程,显示包里程的添加项 二 代码实现: 给select添加change事件 获取当前select的value 根据value判断对象显示其 ...