F. Multi-Colored Segments
观察: 如果某个位置上有大于等于两种不同的颜色,这个位置就可以更新任何颜色的线段的答案。
基于观察就可以通过模拟来解决问题了。
大概就是先离散话的处理一下,跑出每个位置上不同颜色的数量,以及如果这个位置上只有一种颜色,这个颜色是什么。
然后对于每个线段,有 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 。
代码先咕咕咕。
标签:std,unique,color,++,Codeforces,int,vector,Div,826 From: https://www.cnblogs.com/zengzk/p/16786625.html