倍增、DFS序

 

目录

路径最小值​

DFS序练习1

DFS序练习2


路径最小值

 

dfs,相当于一个初始化操作;

以及有一个跟ST表类似的预处理操作

query函数的总体思想就是,利用2个节点的深度来不断向上查最近公共祖先。(前提是2个节点处于同一深度,代码中有体现这一点,可能处于同一深度之后,u就是v的祖先,也可能不是,那就继续向上查找)

// problem :  

#include <bits/stdc++.h>
using namespace std;
#define ll long long
typedef pair<int, int> PII;
#define pb push_back
const int LOGN = 18;
const int N = 201000;
int n, q;
int dep[N], par[N][LOGN + 1], val[N][LOGN + 1];
std::vector<pair<int, int>> e[N];

int query(int u, int v) {
   int ans = 1 << 30;
   if(dep[u] > dep[v]) swap(u, v);
   int d = dep[v] - dep[u];
   for (int j = LOGN; j >= 0; --j) if (d & (1 << j)) {
      ans = min(ans, val[v][j]);
      v = par[v][j];
   }
   if (u == v) return ans;
   for (int j = LOGN; j >= 0; --j) if (par[u][j] != par[v][j]) {
      ans = min(ans, min(val[u][j], val[v][j]));
      u = par[u][j];
      v = par[v][j];
   }
   ans = min(ans, min(val[u][0], val[v][0]));
   return ans;
}
void dfs(int u, int f) {
   dep[u] = dep[f] + 1;
   for (auto p : e[u]) {
      int v = p.first;
      if (v == f) continue;
      par[v][0] = u;
      val[v][0] = p.second;
      dfs(v, u);
   }
}
int main(){
   scanf("%d %d", &n, &q);
   for (int i = 1; i < n; ++i) {
      int u, v, w;
      scanf("%d %d %d", &u, &v, &w);
      e[u].push_back(make_pair(v, w));
      e[v].push_back(make_pair(u, w));
   }
   dfs(1, 0);
   for (int j = 1; j <= LOGN; ++j) {
      for (int u = 1; u <= n; ++u) {
         par[u][j] = par[par[u][j - 1]][j - 1];
         val[u][j] = min(val[u][j - 1], val[par[u][j - 1]][j - 1]);
      }
   }

   for (int i = 1; i <= q; ++i) {
      int u, v;
      scanf("%d %d", &u, &v);
      printf("%d\n", query(u, v));
   }

   return 0;
}

DFS序练习1

DFS序,   将一棵树有序化,特点是子树的序号是连续的,(子树 相当于 涉及区间问题)

c1  维护区间和   

c2 维护点x到根的点权和     (差分) 

为什么为想到差分呢? 可以知道,点x的孩子要到根的话,那么一定会经过点x,所以要加上x这个点的点权, 所以是区间加,并且是单点查询,很自然就想到差分树状数组了。

// problem :  DFS序
#include <bits/stdc++.h>
using namespace std;
#define ll long long
typedef pair<int, int> PII;
#define pb push_back
const int N = 201000;
int n, q, l[N], r[N], tot, a[N];
std::vector<int> e[N];

template<class T>
struct BIT{
    T c[N];
    int size;
    void init(int n){
        size = n;
        for (int i = 1; i <= n; ++i) c[i] = 0;
    }
    inline void modify(int x, T d){
        while(x <= size){
            c[x] += d;
            x += x & -x;
        }
    }
    inline T query(int x){
        T res = 0;
        while(x){
            res += c[x];
            x -= x & -x;
        }
        return res;
    }
};
BIT<ll> c1, c2;
void dfs(int u, int f) {
    l[u] = ++tot;
    for (auto v : e[u]) {
        if (v == f) continue;
        dfs(v, u);
    }
    r[u] = tot;
}
int main(){
    scanf("%d %d", &n, &q);
    for (int i = 1; i < n; ++i) {
        int u, v;
        scanf("%d %d", &u, &v);
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs(1, 0);
    c1.init(n);
    c2.init(n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        c1.modify(l[i], a[i]);
        c2.modify(l[i], a[i]);
        c2.modify(r[i] + 1, -a[i]);
    }

    for (int i = 1; i <= q; ++i) {
        int ty;
        scanf("%d", &ty);
        if (ty == 1) {
            int x, y;
            scanf("%d %d", &x, &y);
            int d = y - a[x];
            a[x] = y;
            c1.modify(l[x], d);
            c2.modify(l[x], d);
            c2.modify(r[x] + 1, -d);
        } else {
            int x;
            scanf("%d", &x);
            printf("%lld %lld\n", c1.query(r[x]) - c1.query(l[x] - 1), c2.query(l[x]));
        }
    }

    return 0;
}

DFS序练习2

 这题跟上一题没有区别,唯一变动的地方就是多了个换根操作,这也是难点之一。

我们一旦换了根,那我们原先的DFS序就会被打乱。这里的解决方法是不进行真正的换根操作,而进行逻辑上的换根。当我们查询x点子树的点权和时,通过x和root之间的关系,来进行模拟换根处理。

这里x和root之间有3种关系:

        1、x 就是 root          ---->  query(n)

        2、root是x的子孙     整体减去 x 的包含root区间的那个子区间

        3、其他                   还是正常的query(r[x]) - query(l[x] - 1)

第1种情况很明显,不画图演示。

第2种情况: 

 第3种情况:

// problem :  DFS序
#include <bits/stdc++.h>
using namespace std;
#define ll long long
typedef pair<int, int> PII;
#define pb push_back
const int N = 201000;
int n, q, l[N], r[N], tot, a[N];
std::vector<int> e[N];
vector<PII> son[N];
template<class T>
struct BIT{
    T c[N];
    int size;
    void init(int n){
        size = n;
        for (int i = 1; i <= n; ++i) c[i] = 0;
    }
    inline void modify(int x, T d){
        while(x <= size){
            c[x] += d;
            x += x & -x;
        }
    }
    inline T query(int x){
        T res = 0;
        while(x){
            res += c[x];
            x -= x & -x;
        }
        return res;
    }
};
BIT<ll> c1;
void dfs(int u, int f) {
    l[u] = ++tot;
    for (auto v : e[u]) {
        if (v == f) continue;
        dfs(v, u);
        son[u].push_back(make_pair(l[v], r[v]));
    }
    r[u] = tot;
}
int main(){
    scanf("%d %d", &n, &q);
    for (int i = 1; i < n; ++i) {
        int u, v;
        scanf("%d %d", &u, &v);
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs(1, 0);
    c1.init(n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        c1.modify(l[i], a[i]);
    }
    int root = 1;
    for (int i = 1; i <= q; ++i) {
        int ty;
        scanf("%d", &ty);
        if (ty == 1) {
            int x, y;
            scanf("%d %d", &x, &y);
            int d = y - a[x];
            a[x] = y;
            c1.modify(l[x], d);
        } else if (ty == 3){
            scanf("%d", &root);

        } else {
            int x;
            scanf("%d", &x);
            if (x == root) {
                printf("%lld\n", c1.query(n));
            } else if (l[x] < l[root] && r[x] >= r[root]) {
                auto seg = *prev(upper_bound(son[x].begin(), son[x].end(), 
                    PII {l[root], r[root]}));
                printf("%lld\n", c1.query(n) - (c1.query(seg.second) - c1.query(seg.first - 1)));
            } else {
                printf("%lld\n", c1.query(r[x]) - c1.query(l[x] - 1));
            }
        }
    }

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xingxg.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值