Solution Set【2023.12.11】
A. interval / GYM102979K Knowledge Is...
首先考虑一种贪心,边扫描区间集边处理匹配关系,若在扫描但未匹配的区间集合中存在可以与当前扫描到的区间匹配的那么就进行匹配。
考虑改进这一算法,首先将区间分为左区间和右区间,那么我们可以将区间按左端点从小到大排序,这样在扫描过的区间中一定只能成为当前区间的左区间,我们可以使用堆维护这个集合,堆顶是集合中右端点最左的区间。这样每扫描到一个区间检查是否可以与堆顶进行配对即可。
发现上述算法有可能将一个右端点不是最合适的区间分给当前区间,故我们考虑引入反悔机制:在当前的区间 \(\mathbf{A}\) 不能在堆中找到一个合适的区间来配对时,我们考虑在已经配对完成的区间对中找到一个右区间右端点最大的 \(\left(\mathbf{L}, \mathbf{R}\right)\),若 \(\mathbf{R}\) 的右端点比 \(\mathbf{A}\) 的靠左,那么我们可以考虑用 \(\mathbf{A}\) 来替换 \(\mathbf{R}\),使得 \(\mathbf{L}\) 与 \(\mathbf{A}\) 配对,\(\mathbf{R}\) 加入扫描但未匹配的区间集合中。可以发现这样操作更有利于后续区间的配对。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
struct Interval {
valueType id;
valueType l, r;
Interval() : id(-1), l(-1), r(-1) {};
Interval(valueType id, valueType l, valueType r) : id(id), l(l), r(r) {};
};
struct CompareL {
bool operator()(Interval const &a, Interval const &b) const {
if (a.l != b.l)
return a.l < b.l;
return a.r < b.r;
}
};
struct CompareR {
bool operator()(Interval const &a, Interval const &b) const {
if (a.r != b.r)
return a.r > b.r;
return a.l > b.l;
}
};
struct IntervalPair {
Interval A, B;
bool operator<(IntervalPair const &other) const {
return B.r < other.B.r;
}
bool operator>(IntervalPair const &other) const {
return B.r > other.B.r;
}
IntervalPair() = default;
IntervalPair(Interval const &A, Interval const &B) : A(A), B(B) {};
IntervalPair(valueType idA, valueType lA, valueType rA, valueType idB, valueType lB, valueType rB) : A(idA, lA, rA),
B(idB, lB,
rB) {};
};
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N, M;
std::cin >> N >> M;
std::vector<Interval> intervals(N);
for (valueType i = 0; i < N; ++i) {
auto &[id, l, r] = intervals[i];
id = i;
std::cin >> l >> r;
}
std::sort(intervals.begin(), intervals.end(), CompareL());
std::priority_queue<IntervalPair, std::vector<IntervalPair>, std::greater<>> match;
std::priority_queue<Interval, std::vector<Interval>, CompareR> remain;
for (auto const &iter : intervals) {
if (!remain.empty() && remain.top().r < iter.l) {
auto const top = remain.top();
remain.pop();
match.emplace(top, iter);
} else if (!match.empty() && match.top().B.r < iter.r) {
auto const top = match.top();
match.pop();
match.emplace(top.A, iter);
remain.push(top.B);
} else {
remain.push(iter);
}
}
ValueVector ans(N, 0);
for (valueType i = 1; i <= M; ++i) {
if (!match.empty()) {
auto const [A, B] = match.top();
match.pop();
ans[A.id] = ans[B.id] = i;
} else if (!remain.empty()) {
auto const top = remain.top();
remain.pop();
ans[top.id] = i;
}
}
for (auto const &iter : ans)
std::cout << iter << ' ';
std::cout << std::endl;
return 0;
}
B. apers / GYM102984J Setting Maps
考虑如何处理 \(K = 1\) 的情况,可以转化为最小割问题:将每个节点 \(u\) 拆分为网络中的两个节点 \(\operatorname{in}_u\) 和 \(\operatorname{out}_u\),连边 \(\operatorname{in}_u \rightarrow \operatorname{out}_u\),容量为 \(C_u\),若这条边被割掉那么就代表这个点被安装了障碍。对于原图中的边 \(\left(u, v\right)\),我们可以连边 \(\operatorname{out}_u \rightarrow \operatorname{in}_u\),容量为 \(\infty\)。网络中 \(\left(\operatorname{in}_S, \operatorname{out}_T\right)\) 的最小割便是答案。
考虑 \(K > 1\) 的情况,可以将图分为 \(K\) 层,那么我们需要处理的限制便是在使得第 \(i < K\) 层的 \(S, T\) 不联通后还需要使得第 \(i + 1\) 层的 \(S, T\) 不联通,也就是说我们需要使得流量可以向上流动,考虑对于每个节点 \(u\) 在第 \(i\) 层的 \(\operatorname{in}_u\),向第 \(i + 1\) 层的 \(\operatorname{out}_u\) 连一条边。这样的话若边 \(\operatorname{in}_u \rightarrow \operatorname{out}_u\) 被割掉,流量可以沿上述边流到上一层中。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::vector<bool> bitset;
constexpr valueType INF = std::numeric_limits<valueType>::max();
void failed() {
std::cout << -1 << std::endl;
std::exit(0);
}
class Dinic {
private:
struct Edge {
public:
typedef std::list<Edge> container;
typedef container::iterator iterator;
valueType to;
valueType cap;
valueType flow;
iterator pair;
Edge() : to(-1), cap(-1), flow(-1), pair() {};
Edge(valueType to, valueType cap, iterator pair = iterator()) : to(to), cap(cap), flow(0), pair(pair) {};
};
typedef std::vector<Edge::container> Graph;
typedef std::vector<Edge::iterator> IterVector;
valueType N;
Graph G;
ValueVector depth;
IterVector start;
bool Initialized;
public:
explicit Dinic(valueType N) : N(N), G(N + 1), depth(N + 1, 0), start(N + 1), Initialized(false) {};
void addEdge(valueType from, valueType to, valueType cap) {
if (__builtin_expect(Initialized, false))
throw std::runtime_error("Dinic: addEdge after init");
G[from].emplace_back(to, cap);
G[to].emplace_back(from, 0);
G[from].back().pair = std::prev(G[to].end());
G[to].back().pair = std::prev(G[from].end());
}
void init() {
if (__builtin_expect(Initialized, false))
throw std::runtime_error("Dinic: init twice");
Initialized = true;
std::fill(depth.begin(), depth.end(), 0);
for (valueType i = 1; i <= N; ++i)
start[i] = G[i].begin();
}
void reset() {
if (__builtin_expect(!Initialized, false))
throw std::runtime_error("Dinic: reset before init");
for (valueType i = 1; i <= N; ++i)
for (auto &iter : G[i])
iter.flow = 0;
std::fill(depth.begin(), depth.end(), 0);
Initialized = false;
}
std::pair<valueType, bitset> maxFlow(valueType S, valueType T) {
if (__builtin_expect(!Initialized, false))
throw std::runtime_error("Dinic: maxFlow before init");
valueType result = 0;
while (bfs(S, T)) {
IterVector begin = start;
valueType const flow = dfs(S, T, std::numeric_limits<valueType>::max(), begin);
if (flow == INF)
failed();
result += flow;
}
bitset inSet(N + 1, false);
bfs(S, T);
for (valueType i = 1; i <= N; ++i)
inSet[i] = depth[i] > 0;
return std::make_pair(result, inSet);
}
private:
bool bfs(valueType S, valueType T) {
std::fill(depth.begin(), depth.end(), 0);
std::queue<valueType> Q;
Q.push(S);
depth[S] = 1;
while (!Q.empty()) {
valueType const u = Q.front();
Q.pop();
for (auto const &e : G[u]) {
if ((e.cap > e.flow) && (!depth[e.to])) {
depth[e.to] = depth[u] + 1;
Q.push(e.to);
}
}
}
return depth[T] > 0;
}
valueType dfs(valueType u, valueType T, valueType flow, IterVector &Begin) {
if (u == T || flow == 0)
return flow;
valueType result = 0;
for (auto &e = Begin[u]; e != G[u].end(); ++e) {
if (e->cap > e->flow && depth[e->to] == depth[u] + 1) {
valueType const f = dfs(e->to, T, std::min(flow - result, e->cap - e->flow), Begin);
e->flow += f;
e->pair->flow -= f;
result += f;
if (result == flow)
return flow;
}
}
return result;
}
};
void calcShortedPath(valueType start, ValueMatrix const &G, ValueVector &dist) {
std::queue<valueType> Q;
std::fill(dist.begin(), dist.end(), -1);
Q.push(start);
dist[start] = 1;
while (!Q.empty()) {
valueType const u = Q.front();
Q.pop();
for (auto const &iter : G[u]) {
if (dist[iter] == -1) {
dist[iter] = dist[u] + 1;
Q.push(iter);
}
}
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
//#ifndef LOCAL_STDIO
// freopen("apers.out", "w", stdout);
// freopen("apers.in", "r", stdin);
//#endif
valueType N, M, K, S, T;
std::cin >> N >> M >> K >> S >> T;
ValueMatrix G(N + 1);
valueType size = 0;
ValueMatrix in(K + 1, ValueVector(N + 1, 0)), out(K + 1, ValueVector(N + 1, 0));
for (valueType i = 1; i <= K; ++i) {
for (valueType j = 1; j <= N; ++j) {
in[i][j] = ++size;
out[i][j] = ++size;
}
}
Dinic dinic(size);
ValueVector weight(N + 1, 0);
for (valueType i = 1; i <= N; ++i) {
std::cin >> weight[i];
for (valueType j = 1; j <= K; ++j)
dinic.addEdge(in[j][i], out[j][i], weight[i]);
}
for (valueType i = 1; i < K; ++i)
for (valueType j = 1; j <= N; ++j)
dinic.addEdge(in[i][j], out[i + 1][j], INF);
for (valueType i = 0; i < M; ++i) {
valueType u, v;
std::cin >> u >> v;
G[u].emplace_back(v);
for (valueType j = 1; j <= K; ++j)
dinic.addEdge(out[j][u], in[j][v], INF);
}
{
ValueVector dist(N + 1, 0);
calcShortedPath(S, G, dist);
if (dist[T] != -1 && dist[T] < K)
failed();
}
dinic.init();
auto const [mincut, inSet] = dinic.maxFlow(in[1][S], out[K][T]);
// std::cout << mincut << std::endl;
ValueVector ans;
for (valueType i = 1; i <= N; ++i)
for (valueType j = 1; j <= K; ++j)
if (inSet[in[j][i]] && !inSet[out[j][i]])
ans.emplace_back(i);
std::cout << ans.size() << std::endl;
for (auto const &iter : ans)
std::cout << iter << ' ';
std::cout << std::endl;
return 0;
}
C. circles / GYM102538H Horrible Cycles
首先由于左部点和右部点内部互不区分,因此我们可以通过如下两个操作来建出最终的二分图:
- 新建一个左部点并将其与现存的所有右部点连边
- 新建一个孤立的右部点
考虑在这个过程中去维护答案,由于最终的环一定是左部点和右部点交替出现的,而左部点在添加时会连接所有存在的右部点,因此我们可以认为是左部点将若干链连接起来成为环的。所以我们对于每一个最终二分图下的环,设 \(f_i\) 表示仅包括现有点集的环中还缺少 \(i\) 个左部点将其连接起来的环的数量。
考虑如何在操作的过程中维护 \(f\) 的值。若添加了一个右部点,那么将其作为一个单独的链加入现存的任意一个链集合,因此有转移:
同时 \(f_1\) 的值会增加 \(1\)。
若添加了一个左部点,那么其可以加入到需要左部点的链集合中的任意一个左部点位置中,有转移
需要注意的是,本题的图中没有重边,因此不含有二元环。但是在上述计算过程中会将每条边也会产生一次贡献。同时每个环在两个方向上均会产生一次贡献。因此 \(f_0\) 减去 \(M\) 后除以 \(2\) 的值便是答案。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
constexpr valueType MOD = 998244353;
template<typename T1, typename T2, typename T3 = valueType>
void Inc(T1 &a, T2 b, const T3 &mod = MOD) {
a = a + b;
if (a >= mod)
a -= mod;
}
template<typename T1, typename T2, typename T3 = valueType>
void Dec(T1 &a, T2 b, const T3 &mod = MOD) {
a = a - b;
if (a < 0)
a += mod;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 mul(T1 a, T2 b, const T3 &mod = MOD) {
return (long long) a * b % mod;
}
template<typename T1, typename T2, typename T3 = valueType>
void Mul(T1 &a, T2 b, const T3 &mod = MOD) {
a = (long long) a * b % mod;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N;
std::cin >> N;
ValueVector A(N + 1), F(N + 1);
for (valueType i = 1; i <= N; ++i) {
valueType x;
std::cin >> x;
++A[x];
}
for (valueType i = 1; i <= N; ++i) {
for (valueType j = N; j >= 2; --j)
Inc(F[j], mul(j - 1, F[j - 1]));
Inc(F[1], 1);
for (valueType t = 0; t < A[i]; ++t) {
for (valueType j = 0; j < N; ++j)
Inc(F[j], mul(j + 1, F[j + 1]));
}
}
valueType ans = F[0];
for (valueType i = 1; i <= N; ++i)
Dec(ans, mul(i, A[i]));
Mul(ans, (MOD + 1) / 2);
std::cout << ans << std::endl;
return 0;
}