• CF 150E Freezing with Style [长链剖分,线段树]


    (sol:)

    给一种大常数 (n log^2 n) 的做法

    考虑二分,由于是中位数,我们就二分这个中位数,(x>=mid)则设为 (1),否则为 (-1) 所以我们只需要找到一条 (sum >= 0) 的路径,这样就有解了,易证。

    长链剖分,让长链变成连续的一段区间 ([dfn_u,dfn_u+len_u-1]),线段树的每个点是对于当前的 (u)

    然后考虑到对于每个 (u) 只需要找到长度在 ([L,R]) 的边,且经过 (u),很显然是从 (u) 的子树里面找,显然你只需要算出来先前每层的(max)存在线段树上面,表示 (dep_v) 的一堆点,到 (u) 的最大路径,然后用 (dfs)+线段树,从下到上更新,每次把 (max) 更新到长链相对的线段树区间 ([dfn_u,dfn_u+len_u-1]) 上面。

    考虑到更新答案什么的,直接暴力更新长链上的信息(复杂度证明在下面)
    即枚举一个长度 (j),然后你另一条边的长度区间是限定的,于是你可以线段树区间查询,所以每次查询的复杂度都是 (log n)

    每次查询完之后更新相同深度的答案,这样就可以保证不会重复了,复杂度仍然是优美的一个 (log)

    复杂度分析

    考虑到它的 (dfs) 是枚举非儿子点的最深深度,而你非儿子点的一定是某个长链的 (top),那么你保证了只会遍历一个点一遍,于是就可以证明这个复杂度是 (O(n)) 的,但是由于你必须要用一个线段树来维护,所以单次的复杂度就到达了 (O(n log n)),外边还需要一个二分,复杂度是 (O(n log^2 n))

    #include <cstdio>
    #include <algorithm>
    
    int read() {
      int x = 0;
      char c = getchar();
      while (c < 48) c = getchar();
      while (c > 47) x = x * 10 + (c - 48), c = getchar();
      return x;
    }
    
    int min(int x, int y) { return x < y ? x : y; }
    int max(int x, int y) { return x > y ? x : y; }
    
    int n, L, R;
    // edge-list
    const int maxn = 2e5 + 52;
    struct edge {
      int v, nxt, w;
    } e[maxn << 1];
    int head[maxn], cnt = 0, val[maxn];
    void add(int u, int v, int w) {
      e[++cnt] = { v, head[u], w }, head[u] = cnt;
      e[++cnt] = { u, head[v], w }, head[v] = cnt;
    }
    
    // dfs
    int len[maxn], son[maxn], wt[maxn];
    void dfs(int u, int fa) {
      for (int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].v;
        if (v == fa) continue;
        dfs(v, u);
        if (len[v] > len[son[u]]) {
          son[u] = v, wt[u] = e[i].w;
        }
      }
      len[u] = len[son[u]] + 1;
    }
    
    int dfn[maxn], idx = 0;
    void dfs(int u) {
      dfn[u] = ++idx;
      if (son[u]) dfs(son[u]);
      for (int i = head[u]; i; i = e[i].nxt)
        if (!dfn[e[i].v]) dfs(e[i].v);
    }
    // smt
    struct node {
      int mx, t;
    } sum[maxn << 2];
    int tag[maxn << 2];
    node merge(const node& x, const node& y) { return x.mx > y.mx ? x : y; }
    
    void clr(int l, int r, int rt) {
      tag[rt] = sum[rt].t = 0, sum[rt].mx = -n;
      if (l == r) return;
      int mid = l + r >> 1;
      clr(l, mid, rt << 1);
      clr(mid + 1, r, rt << 1 | 1);
    }
    
    void pushtag(int rt, int v) { tag[rt] += v, sum[rt].mx += v; }
    void pushd(int rt) {
      if (!tag[rt]) return;
      pushtag(rt << 1, tag[rt]);
      pushtag(rt << 1 | 1, tag[rt]);
      tag[rt] = 0;
    }
    
    node qry(int a, int b, int l, int r, int rt) {
      if (a <= l && r <= b) return sum[rt];
      pushd(rt);
      int mid = l + r >> 1;
      if (b <= mid) return qry(a, b, l, mid, rt << 1);
      if (a > mid) return qry(a, b, mid + 1, r, rt << 1 | 1);
      return merge(qry(a, b, l, mid, rt << 1), qry(a, b, mid + 1, r, rt << 1 | 1));
    }
    
    void change(int a, int b, int l, int r, int rt, int v) {
      if (a <= l && r <= b) {
        pushtag(rt, v);
        return;
      }
      pushd(rt);
      int mid = l + r >> 1;
      if (a <= mid) change(a, b, l, mid, rt << 1, v);
      if (b > mid) change(a, b, mid + 1, r, rt << 1 | 1, v);
      sum[rt] = merge(sum[rt << 1], sum[rt << 1 | 1]);
    }
    
    void modify(int l, int r, int rt, int x, node v) {
      if (l == r) {
        sum[rt] = merge(sum[rt], v);
        return;
      }
      pushd(rt);
      int mid = l + r >> 1;
      if (x <= mid)
        modify(l, mid, rt << 1, x, v);
      else
        modify(mid + 1, r, rt << 1 | 1, x, v);
      sum[rt] = merge(sum[rt << 1], sum[rt << 1 | 1]);
    }
    
    int flag = 0, xx = 0, yy = 0;
    void dfs(int u, int fa, int mid) {
      if (flag) return;
      modify(1, n, 1, dfn[u], { 0, u });
      if (!son[u]) return;
      dfs(son[u], u, mid);
      change(dfn[u] + 1, dfn[u] + len[u] - 1, 1, n, 1, wt[u] >= mid ? 1 : -1);
      if (L < len[u]) {
        node ask = qry(dfn[u] + L, dfn[u] + min(len[u] - 1, R), 1, n, 1);
        if (ask.mx >= 0) {
          flag = 1;
          xx = u, yy = ask.t;
          return;
        }
      }
      for (int i = head[u]; i; i = e[i].nxt) {
        int v = e[i].v;
        if (v == fa || v == son[u]) continue;
        dfs(v, u, mid);
        int w = e[i].w >= mid ? 1 : -1;
        for (int j = 1; j <= len[v]; j++) {
          node c = qry(dfn[v] + j - 1, dfn[v] + j - 1, 1, n, 1);
          c.mx += w;
          if (L - j >= len[u] || j > R) continue;
          node ask = qry(dfn[u] + max(0, L - j), dfn[u] + min(len[u] - 1, R - j), 1, n, 1);
          if (c.mx + ask.mx >= 0) {
            xx = c.t, yy = ask.t;
            flag = 1;
            break;
          }
        }
        for (int j = 1; j <= len[v]; j++) {
          node c = qry(dfn[v] + j - 1, dfn[v] + j - 1, 1, n, 1);
          c.mx += w, modify(1, n, 1, dfn[u] + j, c);
        }
      }
    }
    bool chk(int mid) {
      clr(1, n, 1);
      flag = 0, dfs(1, 0, mid);
      return flag;
    }
    int main() {
      // freopen("testdata.in", "r", stdin);
      n = read(), L = read(), R = read();
      for (int i = 1; i < n; i++) {
        int u = read(), v = read(), w = read();
        add(u, v, w), val[i] = w;
      }
      dfs(1, 0), dfs(1), std ::sort(val + 1, val + n);
      int le = 1, ri = std ::unique(val + 1, val + n) - val - 1;
      int ansx = 0, ansy = 0;
      while (le <= ri) {
        int mid = le + ri >> 1;
        if (chk(val[mid])) {
          le = mid + 1;
          ansx = xx, ansy = yy;
        } else
          ri = mid - 1;
      }
      printf("%d %d
    ", ansx, ansy);
      return 0;
    }
    
  • 相关阅读:
    LEFT JOIN个别问题
    phporjquery生成二维码
    杂项(乌班图、flex的使用实例)
    mysql创建用户并授权,解决1045
    jar运行指定jdk
    Flutter升级版本后,运行项目报错
    spring boot执行jar包指定active profile
    centos7 安装clamav 进行病毒扫描查杀
    CentOS7上安装MySQL 5.7.32(超详细)
    Centos7.5 安装elasticsearch 7.13.2 遇到的问题记录
  • 原文地址:https://www.cnblogs.com/Isaunoya/p/12335059.html
Copyright © 2020-2023  润新知