Codeforces Round #826 (Div. 3)

F. Multi-Colored Segments

观察: 如果某个位置上有大于等于两种不同的颜色,这个位置就可以更新任何颜色的线段的答案。

基于观察就可以通过模拟来解决问题了。

大概就是先离散话的处理一下,跑出每个位置上不同颜色的数量,以及如果这个位置上只有一种颜色,这个颜色是什么。

然后对于每个线段,有 3 种位置可以更新它的答案:

  1. 这个线段覆盖的位置
  2. 这个线段之前的位置
  3. 这个线段之后的位置

对于第 1 种位置,如果可以更新,那么这个线段上必定有超过一种颜色,这个就结合每个位置上不同颜色的数量以及前缀和处理一下就能搞。

对于第 2 种和第 3 种,就线性遍历一下就能跑出来了。以第 2 种为例,根据观察,只需要考虑之前至多两个位置就能包括所有的情况。

AC代码
// Problem: F. Multi-Colored Segments
// Contest: Codeforces - Codeforces Round #826 (Div. 3)
// URL: https://codeforces.com/contest/1741/problem/F
// Memory Limit: 256 MB
// Time Limit: 3000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

#define CPPIO \
  std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);

#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#define ASSERT(x) ;
#define serialize() std::string("")
#endif

using i64 = int64_t;
using u64 = uint64_t;

void Initialize();
void SolveCase(int Case);

int main(int argc, char* argv[]) {
  CPPIO;
  int T = 1;
  std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    SolveCase(t);
  }
  return 0;
}

void Initialize() {}

void SolveCase(int Case) {
  int n;
  std::cin >> n;

  std::vector<int> p(2 * n);
  std::vector<int> l(n), r(n), c(n);
  for (int i = 0; i < n; ++i) {
    int x, y, z;
    std::cin >> x >> y >> z;
    --z;

    l[i] = x;
    r[i] = y;
    c[i] = z;

    p[2 * i] = x;
    p[2 * i + 1] = y;
  }

  std::sort(p.begin(), p.end());
  p.erase(std::unique(p.begin(), p.end()), p.end());
  int m = p.size();
  for (int i = 0; i < n; ++i) {
    l[i] = std::lower_bound(p.begin(), p.end(), l[i]) - p.begin();
    r[i] = std::lower_bound(p.begin(), p.end(), r[i]) - p.begin();
  }

  std::vector<int> num_unique_color_at(m, 0);
  std::vector<int> unique_color_at(m, -1);
  std::vector<int> num_of_color(n, 0);
  int num_unique_color = 0;
  std::set<int> unique_colors;
  std::vector<std::vector<std::array<int, 2>>> color_start_at(m);
  std::vector<std::vector<std::array<int, 2>>> color_end_at(m);
  for (int i = 0; i < n; ++i) {
    logd(l[i], r[i], c[i]);
    color_start_at[l[i]].push_back({c[i], i});
    color_end_at[r[i]].push_back({c[i], i});
  }
  for (int i = 0; i < m; ++i) {
    std::sort(color_start_at[i].begin(), color_start_at[i].end());
    std::sort(color_end_at[i].begin(), color_end_at[i].end());
  }
  for (int i = 0; i < m; ++i) {
    for (auto [color, seg_id] : color_start_at[i]) {
      ++num_of_color[color];
      if (num_of_color[color] == 1) {
        logd(i, color);
        ++num_unique_color;
        unique_colors.insert(color);
      }
    }

    num_unique_color_at[i] = num_unique_color;
    if (num_unique_color == 1) {
      unique_color_at[i] = *unique_colors.begin();
    }

    for (auto [color, seg_id] : color_end_at[i]) {
      --num_of_color[color];
      if (num_of_color[color] == 0) {
        logd(i, color);
        --num_unique_color;
        unique_colors.erase(color);
      }
    }
  }
  std::vector<int> has_more_than_two_color(m, 0);
  for (int i = 0; i < m; ++i) {
    has_more_than_two_color[i] = num_unique_color_at[i] > 1;
  }
  std::vector<int> accumulate_h = has_more_than_two_color;
  for (int i = 1; i < m; ++i)
    accumulate_h[i] += accumulate_h[i - 1];
  logd(num_unique_color_at);
  logd(has_more_than_two_color);

  std::vector<int> ans(n, std::numeric_limits<int>::max());
  {
    for (int i = 0; i < n; ++i) {
      int L = l[i];
      int R = r[i];
      int S = accumulate_h[R] - (L == 0 ? 0 : accumulate_h[L - 1]);
      if (S > 0)
        ans[i] = 0;
    }
  }
  {
    // -1 stands for invalid, n stands for more than 2.
    int c1 = -1, i1 = -1;
    int c2 = -1, i2 = -1;
    for (int i = 0; i < m; ++i) {
      int unique_color_at_i = unique_color_at[i];
      for (auto [color, seg_id] : color_start_at[i]) {
        logd(color, seg_id, c1, i1, c2, i2);

        if (c1 != -1 && (c1 == n || color != c1)) {
          ans[seg_id] = std::min(ans[seg_id], p[i] - p[i1]);
        }
        if (c2 != -1 && (c2 == n || color != c2)) {
          ans[seg_id] = std::min(ans[seg_id], p[i] - p[i2]);
        }
      }

      if (has_more_than_two_color[i] || c1 != unique_color_at_i) {
        c2 = c1, i2 = i1;
        c1 = has_more_than_two_color[i] ? n : unique_color_at_i, i1 = i;
      } else {
        i1 = i;
      }
    }
  }
  {
    // -1 stands for invalid, n stands for more than 2.
    int c1 = -1, i1 = -1;
    int c2 = -1, i2 = -1;
    for (int i = m - 1; i >= 0; --i) {
      int unique_color_at_i = unique_color_at[i];
      for (auto [color, seg_id] : color_end_at[i]) {
        unique_color_at_i = color;

        if (c1 != -1 && (c1 == n || color != c1)) {
          ans[seg_id] = std::min(ans[seg_id], -p[i] + p[i1]);
        }
        if (c2 != -1 && (c2 == n || color != c2)) {
          ans[seg_id] = std::min(ans[seg_id], -p[i] + p[i2]);
        }
      }

      if (has_more_than_two_color[i] || c1 != unique_color_at_i) {
        c2 = c1, i2 = i1;
        c1 = has_more_than_two_color[i] ? n : unique_color_at_i, i1 = i;
      } else {
        i1 = i;
      }
    }
  }

  for (int i = 0; i < n; ++i)
    std::cout << ans[i] << " \n"[i + 1 == n];
}

G. Kirill and Company

做法1

注意到至多只有 \(6\) 个人没有车,所以建图跑费用流的话,至多跑 \(6\) 次增广之后就能得到答案。

AC代码
// Problem: G. Kirill and Company
// Contest: Codeforces - Codeforces Round #826 (Div. 3)
// URL: https://codeforces.com/contest/1741/problem/G
// Memory Limit: 256 MB
// Time Limit: 3000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>

#define CPPIO \
  std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);

#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#define ASSERT(x) ;
#define serialize() std::string("")
#endif

using i64 = int64_t;
using u64 = uint64_t;

void Initialize();
void SolveCase(int Case);

int main(int argc, char* argv[]) {
  CPPIO;
  int T = 1;
  std::cin >> T;
  for (int t = 1; t <= T; ++t) {
    SolveCase(t);
  }
  return 0;
}

void Initialize() {}

template <typename DistanceType,
          typename Comp = std::greater<>,
          typename Edge = std::pair<DistanceType, int>,
          typename Node = std::pair<DistanceType, int>>
std::vector<DistanceType> Dijkstra(const std::vector<std::vector<Edge>>& g,
                                   int s) {
  const DistanceType INF = std::numeric_limits<DistanceType>::max();
  const int n = g.size();
  const Comp comp;

  std::vector<DistanceType> dis(n, INF);
  std::vector<bool> vis(n, false);

  std::priority_queue<Node, std::vector<Node>, Comp> q;
  dis[s] = 0;
  q.push(Node(dis[s], s));
  while (!q.empty()) {
    auto [c, u] = q.top();
    q.pop();

    if (vis[u])
      continue;
    vis[u] = true;

    for (auto [w, v] : g[u]) {
      if (comp(dis[v], c + w)) {
        dis[v] = c + w;
        q.push(Node(dis[v], v));
      }
    }
  }

  return dis;
}

template <typename CapacityType, typename CostType>
class MinCostMaxFlowGraph {
 private:
  struct Edge {
    int from, to;
    CapacityType capacity;
    CostType cost;
    Edge() {}
    Edge(int _from, int _to, CapacityType _capacity, CostType _cost)
        : from(_from), to(_to), capacity(_capacity), cost(_cost) {}
  };
  int n_;
  int m_;
  std::vector<Edge> edges_;
  std::vector<std::vector<int>> adjacent_;

 public:
  MinCostMaxFlowGraph(int n) : n_(n), m_(0), edges_(0), adjacent_(n) {}

  void AddEdge(int u, int v, CapacityType capacity, CostType cost) {
    assert(0 <= u and u < n_);
    assert(0 <= v and v < n_);

    edges_.push_back(Edge(u, v, capacity, cost));
    adjacent_[u].push_back(m_);
    ++m_;

    edges_.push_back(Edge(v, u, 0, -cost));
    adjacent_[v].push_back(m_);
    ++m_;
  }

  std::pair<CapacityType, CostType> EK(int src, int dst) {
    const static CapacityType INF = std::numeric_limits<CapacityType>::max();

    std::vector<CostType> h(n_);
    std::vector<bool> inq(n_);
    std::function<void()> spfa = [&]() -> void {
      std::fill(h.begin(), h.end(), INF);

      std::queue<int> q;
      q.push(src);
      inq[src] = true;
      h[src] = 0;
      while (!q.empty()) {
        int u = q.front();
        q.pop();
        inq[u] = false;

        for (int edge_id : adjacent_[u]) {
          auto [from, to, capacity, cost] = edges_[edge_id];
          if (capacity <= 0 || h[to] <= h[from] + cost)
            continue;
          h[to] = h[from] + cost;
          if (!inq[to]) {
            q.push(to);
            inq[to] = true;
          }
        }
      }
    };

    std::vector<CostType> d(n_);
    std::vector<bool> vis(n_);
    std::vector<int> prev(n_);
    std::function<bool()> dijkstra = [&]() -> bool {
      std::fill(d.begin(), d.end(), INF);
      std::fill(vis.begin(), vis.end(), false);

      std::priority_queue<std::pair<CostType, int>> q;
      d[src] = 0;
      q.push(std::make_pair(-d[src], src));

      while (!q.empty()) {
        auto [_, u] = q.top();
        q.pop();

        if (vis[u])
          continue;
        vis[u] = true;

        for (int edge_id : adjacent_[u]) {
          auto [from, to, capacity, cost] = edges_[edge_id];
          CostType new_cost = cost + h[from] - h[to];

          if (vis[to] || capacity <= 0 || d[to] <= d[from] + new_cost)
            continue;

          d[to] = d[from] + new_cost;
          prev[to] = edge_id;
          q.push(std::make_pair(-d[to], to));
        }
      }

      return d[dst] != INF;
    };

    spfa();

    CapacityType max_flow = 0;
    CostType min_cost = 0;
    while (dijkstra()) {
      CostType old_cost = min_cost;
      CapacityType augment = INF;
      for (int p = dst; p != src; p = edges_[prev[p] ^ 1].to) {
        augment = std::min(augment, edges_[prev[p]].capacity);
      }

      max_flow += augment;
      for (int p = dst; p != src; p = edges_[prev[p] ^ 1].to) {
        min_cost += edges_[prev[p]].cost * augment;
        edges_[prev[p]].capacity -= augment;
        edges_[prev[p] ^ 1].capacity += augment;
      }

      for (int i = 0; i < n_; ++i)
        h[i] += d[i];

      if (old_cost == min_cost)
        break;
    }

    return {max_flow, min_cost};
  }
};

void SolveCase(int Case) {
  int n, m;
  std::cin >> n >> m;

  std::vector<std::vector<std::pair<int, int>>> g(n);
  for (int i = 0; i < m; ++i) {
    int u, v;
    std::cin >> u >> v;
    --u, --v;
    g[u].push_back({1, v});
    g[v].push_back({1, u});
  }

  int f;
  std::cin >> f;
  std::vector<int> h(f);
  std::vector<int> count(n, 0);
  for (int i = 0; i < f; ++i) {
    std::cin >> h[i];
    --h[i];

    ++count[h[i]];
  }

  int k;
  std::cin >> k;
  std::vector<int> count_p(n, 0);
  for (int i = 0; i < k; ++i) {
    int x;
    std::cin >> x;
    --x;

    --count[h[x]];
    ++count_p[h[x]];
  }
  logd(count, count_p);

  int ans = k;
  std::vector<bool> has_car(n, true);
  for (int i = 0; i < n; ++i) {
    if (count_p[i] > 0) {
      if (count[i] == 0) {
        has_car[i] = false;
      } else {
        ans -= count_p[i];
      }
    }
  }

  auto dis = Dijkstra<int>(g, 0);
  logd(dis);

  const int INF = 0x3f3f3f3f;
  MinCostMaxFlowGraph<i64, i64> mcmf(2 * n + 1);
  int s = 2 * n;
  for (int i = 0; i < n; ++i) {
    mcmf.AddEdge(i, n + i, INF, 0);
    if (!has_car[i]) {
      logd(i, count_p[i]);
      mcmf.AddEdge(i, n + i, 1, -count_p[i]);
    }
  }
  for (int u = 0; u < n; ++u) {
    for (auto [w, v] : g[u]) {
      if (dis[u] == dis[v] + 1) {
        mcmf.AddEdge(n + u, v, INF, 0);
      }
    }
  }
  for (int i = 1; i < n; ++i) {
    if (has_car[i]) {
      logd(i);
      mcmf.AddEdge(s, i, count[i], 0);
    }
  }

  int min_cost = mcmf.EK(s, n + 0).second;
  assert(min_cost <= 0);
  ans += min_cost;
  assert(ans >= 0);
  std::cout << ans << "\n";
}

做法2

学了下 jiangly 的代码,大概就是对于每个有车的人 \(i\) ,枚举没车的人构成的集合 \(s\) ,看 \(i\) 能否顺路送所有 \(s\) 中的人回家,这个是单人的情况。多人的情况就是再跑个状压 DP 。

代码先咕咕咕。

posted @ 2022-10-13 00:14  _Backl1ght  阅读(92)  评论(0编辑  收藏  举报