ICode9

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

[AGC014E] Blue and Red Tree

2021-01-26 23:02:24  阅读:141  来源: 互联网

标签:Blue std return int top Tree ++ AGC014E fa


  • 给定一棵 \(n\) 个节点的树,一开始所有边都是蓝色的。每次选择一条所有边都是蓝色的路径,删掉其中一条边,然后在路径的两个端点之间连一条红边。求最后能不能得到目标形态(都是红边)的树。
  • \(n \leq 10^5\)。

链覆盖

把所有选择的路径都覆盖一遍,那么第一次删除的边就是只覆盖了一次的边,删掉对应路径后,再取只覆盖了一次的边……以此类推,这种操作如果能进行 \(n-1\) 次就说明可行。

只有链操作,树剖或者LCT之类的随便写。

#include <bits/stdc++.h>
#define perr(a...) fprintf(stderr, a)
#define dbg(a...) perr("\033[32;1m"), perr(a), perr("\033[0m")
template <class T, class U>
inline bool smin(T &x, const U &y) {
  return y < x ? x = y, 1 : 0;
}
template <class T, class U>
inline bool smax(T &x, const U &y) {
  return x < y ? x = y, 1 : 0;
}

using LL = long long;
using PII = std::pair<int, int>;

constexpr int N(1e5 + 5);
int n;
std::vector<int> g[N];
int fa[N], in[N], siz[N], son[N], top[N], dep[N];
void dfs1(int x) {
  siz[x] = 1;
  for (int y : g[x]) {
    if (y == fa[x]) continue;
    fa[y] = x;
    dep[y] = dep[x] + 1;
    dfs1(y);
    siz[x] += siz[y];
    if (siz[y] > siz[son[x]]) son[x] = y;
  }
}
void dfs2(int x) {
  in[x] = ++in[0];
  if (!son[x]) return;
  top[son[x]] = top[x];
  dfs2(son[x]);
  for (int y : g[x]) {
    if (y == fa[x] || y == son[x]) continue;
    top[y] = y;
    dfs2(y);
  }
}
PII min[N << 2], e[N];
int val[N << 2], add[N << 2];

#define ls o << 1
#define rs o << 1 | 1

inline void pushup(int o) {
  min[o] = std::min(min[ls], min[rs]);
}
inline void pushdown(int o) {
  if (add[o]) {
    add[ls] += add[o], add[rs] += add[o];
    min[ls].first += add[o], min[rs].first += add[o];
    add[o] = 0;
  }
}
void del(int o, int l, int r, int x) {
  if (l == r) { min[o].first = 1e9; return; }
  pushdown(o);
  int m = l + r >> 1;
  x <= m ? del(ls, l, m, x) : del(rs, m + 1, r, x);
  pushup(o);
}
void update(int o, int l, int r, int x, int y, int z, int v) {
  if (x <= l && r <= y) {
    add[o] += z;
    min[o].first += z;
    val[o] ^= v;
    return;
  }
  pushdown(o);
  int m = l + r >> 1;
  if (x <= m) update(ls, l, m, x, y, z, v);
  if (y > m) update(rs, m + 1, r, x, y, z, v);
  pushup(o);
}
int ask(int o, int l, int r, int x) {
  if (l == r) return val[o];
  int m = l + r >> 1;
  pushdown(o);
  return (x <= m ? ask(ls, l, m, x) : ask(rs, m + 1, r, x)) ^ val[o];
}
void build(int o, int l, int r) {
  if (l == r) {
    min[o] = {0, l};
    return;
  }
  int m = l + r >> 1;
  build(ls, l, m), build(rs, m + 1, r);
  pushup(o);
}
void update(int x, int y, int z, int v) {
  for (; top[x] ^ top[y]; x = fa[top[x]]) {
    if (dep[top[x]] < dep[top[y]]) std::swap(x, y);
    update(1, 2, n, in[top[x]], in[x], z, v);
  }
  if (x == y) return;
  if (dep[x] > dep[y]) std::swap(x, y);
  update(1, 2, n, in[x] + 1, in[y], z, v);
}
int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  std::cin >> n;
  for (int i = 1, x, y; i < n; i++) {
    std::cin >> x >> y;
    g[x].push_back(y), g[y].push_back(x);
  }
  dfs1(1), top[1] = 1, dfs2(1);
  build(1, 2, n);
  for (int i = 1; i < n; i++) {
    std::cin >> e[i].first >> e[i].second;
    update(e[i].first, e[i].second, 1, i);
  }
  for (int i = 1; i < n; i++) {
    if (min[1].first != 1) return puts("NO"), 0;
    int x = min[1].second, y = ask(1, 2, n, x);
    assert(y);
    auto [u, v] = e[y];  
    update(u, v, -1, y);
    del(1, 2, n, x);
  }
  puts("YES");
  return 0;
}

启发式合并

对于第一棵树,最终状态是边都被删光了,只剩 \(n\) 个点。

倒着考虑,删边变成加边,具体点就是每次选择两个不连通的点在它们之间加边,对应在第二棵树上就是删一条端点分别在两个联通块内的边。

要在两棵树中分别找到这两条边,他们的共同点是连接了同样的两个联通块。

用并查集维护联通块,维护联通块连向外面的点集,用 std::map 统计两端点在不同联通块的边的个数。

#include <bits/stdc++.h>
#define perr(a...) fprintf(stderr, a)
#define dbg(a...) perr("\033[32;1m"), perr(a), perr("\033[0m")
template <class T, class U>
inline bool smin(T &x, const U &y) {
  return y < x ? x = y, 1 : 0;
}
template <class T, class U>
inline bool smax(T &x, const U &y) {
  return x < y ? x = y, 1 : 0;
}

using LL = long long;
using PII = std::pair<int, int>;


int main() {
  std::ios::sync_with_stdio(false);
  std::cin.tie(nullptr);
  int n;
  std::cin >> n;
  std::vector<std::vector<int>> g(n);
  std::map<PII, int> p;
  std::queue<PII> q;
  std::vector<int> fa(n);
  std::iota(fa.begin(), fa.end(), 0);
  auto find = [&](int x) {
    while (fa[x] != x) x = fa[x] = fa[fa[x]];
    return x;
  };
  for (int i = 2, x, y; i < n + n; i++) {
    std::cin >> x >> y;
    x--, y--;
    if (x > y) std::swap(x, y);
    g[x].push_back(y), g[y].push_back(x);
    if (++p[PII(x, y)] == 2) q.emplace(x, y);
  }
  for (int i = 1; i < n; ) {
    if (q.empty()) return puts("NO"), 0;
    auto [x, y] = q.front();
    q.pop();
    x = find(x), y = find(y);
    if (p[PII(x, y)] < 2) continue;
    assert(x != y);
    if (g[x].size() > g[y].size()) std::swap(x, y);
    fa[x] = y;
    for (int v : g[x]) {
      v = find(v);
      if (v == y) continue;
      --p[std::minmax(x, v)];
      if (++p[std::minmax(y, v)] == 2) q.push(std::minmax(y, v));
      g[y].push_back(v);      
    }
    
    i++;
  }
  puts("YES");
  return 0;
}


标签:Blue,std,return,int,top,Tree,++,AGC014E,fa
来源: https://www.cnblogs.com/HolyK/p/14332874.html

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

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

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

ICode9版权所有