zoukankan      html  css  js  c++  java
  • 洛谷 P3979 遥远的国度

    思路

    一道树链剖分好题。

    爆零小技巧:设置最大值比给出序列的最大值还小

    如果没有修改根的操作,那必定是个树链剖分板子题,只需要求线段树区间最小值,以及进行区间修改即可。

    但是,有了换根操作就有了麻烦,不过可以发现一个小性质,那就是不管根是什么,一个点 (x) 到另一点 (y) 的路径都是不变的。

    所以就可以直接以(1)为根进行树剖,每次修改时像普通树剖一样进行修改,所以现在的问题就是如何处理查询操作。

    设当前要查询的节点为 (x),当前的根为 (root),在查询时分多种情况:

    • (root) 等于 (x) 时,直接输出全局最小值
    • (root) 不在 (x) 的子树中时,(x) 的子树还是以 (1) 为根时的子树,直接对 (x) 进行查询
    • (root)(x) 的子树当中时,(x) 的子树中不能访问到的是 (root) 所在支链,求出 (x)(root) 支链处的儿子 (sonn),因为在 (dfn) 序中,一个点以及其子树内所有点的编号是连续的,所以直接求(1sim dfn[sonn]-1) 中的最小值和 (dfn[sonn] + siz[sonn]sim n)中的最小值,取 (min) 即可

    时间复杂度 (O(nlog^2 n))

    代码

    /*
    Author:loceaner
    */
    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    
    const int A = 2e5 + 11;
    const int B = 1e6 + 11;
    const int mod = 1e9 + 7;
    const int inf = 0x7fffffff;
    
    inline int read() {
      char c = getchar();
      int x = 0, f = 1;
      for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
      for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
      return x * f;
    }
    
    int n, m, root, cnt, pre[A], val[A], head[A]; 
    struct node { int to, nxt; } e[A << 1];
    
    inline void add(int from, int to) {
      e[++cnt].to = to;
      e[cnt].nxt = head[from];
      head[from] = cnt;
    }
    
    namespace Seg {
      #define lson rt << 1
      #define rson rt << 1 | 1
      
      struct tree { int l, r, minn, lazy; } t[A << 2];
      
      inline void pushup(int rt) {
        t[rt].minn = min(t[lson].minn, t[rson].minn);
      }
      
      inline void pushdown(int rt) {
        t[lson].minn = t[rt].lazy;
        t[rson].minn = t[rt].lazy;
        t[lson].lazy = t[rt].lazy;
        t[rson].lazy = t[rt].lazy;
        t[rt].lazy = 0;
      }
      
      void build(int rt, int l, int r) {
        t[rt].l = l, t[rt].r = r, t[rt].lazy = 0;
        if (l == r) {
          t[rt].minn = val[pre[l]];
          return;
        }
        int mid = (l + r) >> 1;
        build(lson, l, mid), build(rson, mid + 1, r);
        pushup(rt);
      }
    
      void update(int rt, int l, int r, int k) {
        if (l <= t[rt].l && t[rt].r <= r) {
          t[rt].lazy = t[rt].minn = k; 
          return;
        }
        if (t[rt].lazy) pushdown(rt);
        int mid = (t[rt].l + t[rt].r) >> 1;
        if (l <= mid) update(lson, l, r, k);
        //debug: l, r 写成 l, mid 
        if (r > mid) update(rson, l, r, k);
        //debug: l, r 写成 mid + 1, r 
        pushup(rt);
      }
      
      int query(int rt, int l, int r) {
        if (l <= t[rt].l && t[rt].r <= r) return t[rt].minn;
        if (t[rt].lazy) pushdown(rt);
        int mid = (t[rt].l + t[rt].r) >> 1, ans = inf;
        if (l <= mid) ans = min(ans, query(lson, l, r));
        //debug: l, r 写成 l, mid 
        if (r > mid) ans = min(ans, query(rson, l, r));
        //debug: l, r 写成 mid + 1, r 
        return ans;
        //爆零小技巧:在有返回值的函数中不加return 
      }
    }
    
    int dfscnt, dep[A], fa[A], siz[A], son[A], dfn[A], top[A];
    
    void prepare(int x, int f) {
      siz[x] = 1, fa[x] = f, dep[x] = dep[f] + 1;
      for (int i = head[x]; i; i = e[i].nxt) {
        int to = e[i].to;
        if (to == f) continue;
        prepare(to, x), siz[x] += siz[to];
        if (siz[to] > siz[son[x]]) son[x] = to;
      }
    }
    
    void dfs(int x, int tp) {
      dfn[x] = ++dfscnt, pre[dfscnt] = x, top[x] = tp;
      if (son[x]) dfs(son[x], tp);
      for (int i = head[x]; i; i = e[i].nxt) {
        int to = e[i].to;
        if (to == fa[x] || to == son[x]) continue;
        dfs(to, to);
      } 
    } 
    
    inline void upd(int x, int y, int val) {
      while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        Seg::update(1, dfn[top[x]], dfn[x], val);
        x = fa[top[x]];
      }
      if (dep[x] > dep[y]) swap(x, y);
      Seg::update(1, dfn[x], dfn[y], val); return;
    }
    
    inline int prove(int x) {
      if (root == x) return 1;
      if (dfn[root] <= dfn[x] || dfn[root] > dfn[x] + siz[x] - 1) return 2;
      return 0;
    }
    
    inline void solve(int x) {
      int flag = prove(x);
    //  cout << flag << " ";
      if (flag == 1) cout << Seg::t[1].minn << '
    ';
      else if (flag == 2) cout << Seg::query(1, dfn[x], dfn[x] + siz[x] - 1) << '
    ';
      else {
        int now = root, sonn = 0;
        while (top[now] != top[x]) {
          if (fa[top[now]] == x) { sonn = top[now]; break; }
          now = fa[top[now]]; 
        }
        if (!sonn) sonn = son[x];
        int ans = Seg::query(1, 1, dfn[sonn] - 1);
        if (dfn[sonn] + siz[sonn] - 1 != n) 
          ans = min(ans, Seg::query(1, dfn[sonn] + siz[sonn], n));
        cout << ans << '
    ';
      }
    }
    
    int main() {
    //  freopen("a.in", "r", stdin);
    //  freopen("1.out", "w", stdout);
      n = read(), m = read();
      for (int i = 1; i < n; i++) {
        int x = read(), y = read();
        add(x, y), add(y, x);
      }
      for (int i = 1; i <= n; i++) val[i] = read();
      prepare(1, 0), dfs(1, 1), Seg::build(1, 1, n);
      root = read();
      while (m--) {
        int opt = read(), x = read(), y, val;
        if (opt == 1) root = x;
        else if (opt == 2) y = read(), val = read(), upd(x, y, val);
        else if (opt == 3) solve(x);
      }
      return 0;
    }
    
  • 相关阅读:
    视频:JDBCRDD源码及自定义JDBCRDD的分区策略
    聊聊spark-submit的几个有用选项
    线性求第k大
    汇编基础 第一章_总线
    长度不超过n的连续最大和___优先队列
    ISAP
    次小生成树
    k短路
    求出欧拉回路,欧拉路径
    拓扑排序bfs_dfs
  • 原文地址:https://www.cnblogs.com/loceaner/p/13522181.html
Copyright © 2011-2022 走看看