ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2020-02-20 12:02:57  阅读:216  来源: 互联网

标签:长链 Style return 剖分 int 复杂度 mid len dfn


\(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\n", ansx, ansy);
  return 0;
}

标签:长链,Style,return,剖分,int,复杂度,mid,len,dfn
来源: https://www.cnblogs.com/Isaunoya/p/12335059.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有