Solution Set【2023.12.4】
来衡实了,感觉良好。
[NOIP2023] 三值逻辑
一直以为是写假了,结果是写挂了,没有判自环的同时 \(u, v\) 输入反了。
考虑对于每个变量的每个版本均开一个节点,那么赋值关系可以用有向边表示,可以发现最终得到的一定是若干外向基环树和若干外向树组成的图。且被 \(\tt{T,F,U}\) 三种指令赋值的节点一定是外向树的根,不会引起冲突。因此只需要考虑所有的基环树即可。从基环树的环开始递归染色,若冲突则整课树的值均为 \(\tt{U}\)。
Code
#include <bits/stdc++.h>
typedef int valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::vector<bool> bitset;
typedef std::queue<valueType> ValueQueue;
typedef std::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;
typedef std::vector<PairVector> PairMatrix;
valueType N, M, S;
ValueVector color, now, degree;
PairMatrix G;
ValueMatrix revG;
bitset undefined;
bool push(valueType x) {
bool success = true;
undefined[x] = false;
for (auto const &[to, w] : G[x]) {
if (undefined[to]) {
color[to] = color[x] * w;
undefined[to] = false;
if (!push(to))
success = false;
} else {
if (color[to] != color[x] * w)
success = false;
}
}
return success;
}
void pushZero(valueType x) {
color[x] = 0;
for (auto const &[to, w] : G[x]) {
if (color[to] != 0)
pushZero(to);
}
}
void solve() {
std::cin >> N >> M;
S = N + M;
color.resize(S + 1, 1);
G.resize(S + 1);
revG.resize(S + 1);
undefined.resize(S + 1, true);
degree.resize(S + 1, 0);
now.resize(N + 1);
std::iota(now.begin(), now.end(), 0);
valueType count = N;
for (valueType m = 0; m < M; ++m) {
char type;
std::cin >> type;
if (type == 'T') {
valueType x;
std::cin >> x;
now[x] = ++count;
undefined[now[x]] = false;
color[now[x]] = 1;
} else if (type == 'F') {
valueType x;
std::cin >> x;
now[x] = ++count;
undefined[now[x]] = false;
color[now[x]] = -1;
} else if (type == 'U') {
valueType x;
std::cin >> x;
now[x] = ++count;
undefined[now[x]] = false;
color[now[x]] = 0;
} else if (type == '+') {
valueType u, v;
std::cin >> v >> u;
valueType const from = now[u];
now[v] = ++count;
G[from].emplace_back(now[v], 1);
assert(undefined[now[v]]);
revG[now[v]].emplace_back(from);
++degree[from];
} else {
valueType u, v;
std::cin >> v >> u;
valueType const from = now[u];
now[v] = ++count;
G[from].emplace_back(now[v], -1);
assert(undefined[now[v]]);
revG[now[v]].emplace_back(from);
++degree[from];
}
}
for (valueType i = 1; i <= N; ++i) {
G[now[i]].emplace_back(i, 1);
assert(undefined[i]);
revG[i].emplace_back(now[i]);
++degree[now[i]];
}
assert(count == S);
S = count;
bitset const undefinedCopy = undefined;
for (valueType i = 1; i <= S; ++i) {
if (undefinedCopy[i])
continue;
assert(revG[i].empty());
undefined[i] = false;
if (!push(i))
std::abort();
}
ValueQueue Q;
for (valueType i = 1; i <= S; ++i) {
if (degree[i] == 0)
Q.push(i);
}
while (!Q.empty()) {
valueType const x = Q.front();
Q.pop();
for (auto const &iter : revG[x])
if (--degree[iter] == 0)
Q.push(iter);
}
for (valueType i = 1; i <= S; ++i) {
if (undefined[i] && degree[i] != 0) {
color[i] = 1;
undefined[i] = false;
if (!push(i))
pushZero(i);
}
}
for (valueType i = 1; i <= S; ++i)
assert(!undefined[i]);
valueType ans = 0;
for (valueType i = 1; i <= N; ++i) {
assert(color[i] == color[now[i]]);
if (color[i] == 0)
++ans;
}
std::cout << ans << std::endl;
}
void clear() {
N = M = S = 0;
color.clear();
now.clear();
degree.clear();
G.clear();
undefined.clear();
revG.clear();
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
// #ifndef LOCAL_STDIO
// freopen("tribool.in", "r", stdin);
// freopen("tribool.out", "w", stdout);
// #endif
valueType ID, T;
std::cin >> ID >> T;
for (valueType testcase = 0; testcase < T; ++testcase) {
clear();
solve();
}
return 0;
}
[NOIP2023] 双序列拓展
首先考虑一个 \(\mathcal{O}(nm)\) 的 \(\tt{DP}\),设 \(f_{i, j}\) 表示是否存在方案使得 \(X_i\) 与 \(Y_j\) 配对。那么有转移:
考虑上述的过程在干什么,设 \(X_1 < Y_1\),那么我们可以构造出一个 \(n \times m\) 的网格 \(A\),满足 \(A_{i, j} = \left[X_i < Y_j\right]\)。
那么 \(f_{i, j}\) 实质上表示的就是从 \(\left(1, 1\right)\) 只向下,右或右下移动是否可以到达 \(\left(i, j\right)\)。
考虑特殊性质下的方格图,设 \(X_{\min}\) 表示 \(X\) 的最小值,\(X_{\max}\) 表示 \(X\) 的最大值,\(Y_{\min}\) 表示 \(Y\) 的最小值,\(Y_{\max}\) 表示 \(Y\) 的最大值。
那么如果 \(X_{\min} \ge Y_{\min}\) 或者 \(X_{\max} \ge Y_{\max}\),那么一定无解。
所以有 \(X_{\min} < Y_{\min}, X_{\max} < Y_{\max}\)。
转化到网格图上,可以发现图的最后一行和最后一列的值均为 \(1\),所以只要可以从 \(\left(1, 1\right)\) 到达最后一行或最后一列的任意一个格子均可以到达 \(\left(n, m\right)\)。发现此时最后一行或最后一列可以视为一个边界。考虑前 \(n - 1\) 行和 \(m - 1\) 列,发现我们可以继续寻找出两个序列的极值并继续收缩边界,直到收缩到第 \(1\) 行或第 \(1\) 列或在中途发现无解。
在没有特殊限制的情况下可以通过序列极值构成的十字架将网格分为四部分,对于左上和右下部分分别处理即可。
Code
#include <bits/stdc++.h>
typedef int valueType;
typedef std::vector<valueType> ValueVector;
typedef std::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;
namespace MAIN {
PairVector PreMaxA, PreMinA, SufMaxA, SufMinA;
PairVector PreMaxB, PreMinB, SufMaxB, SufMinB;
bool checkUp(valueType X, valueType Y, valueType N, valueType M) {
if (X == 1 || Y == 1)
return true;
auto const &[MinA, MinA_Pos] = PreMinA[X - 1];
auto const &[MaxA, MaxA_Pos] = PreMaxA[X - 1];
auto const &[MinB, MinB_Pos] = PreMinB[Y - 1];
auto const &[MaxB, MaxB_Pos] = PreMaxB[Y - 1];
if (MinA < MinB)
return checkUp(MinA_Pos, Y, N, M);
if (MaxB > MaxA)
return checkUp(X, MaxB_Pos, N, M);
return false;
}
bool checkDown(valueType X, valueType Y, valueType N, valueType M) {
if (X == N || Y == M)
return true;
auto const &[MinA, MinA_Pos] = SufMinA[X + 1];
auto const &[MaxA, MaxA_Pos] = SufMaxA[X + 1];
auto const &[MinB, MinB_Pos] = SufMinB[Y + 1];
auto const &[MaxB, MaxB_Pos] = SufMaxB[Y + 1];
if (MinA < MinB)
return checkDown(MinA_Pos, Y, N, M);
if (MaxB > MaxA)
return checkDown(X, MaxB_Pos, N, M);
return false;
}
bool solve(valueType N, valueType M, ValueVector const &A, ValueVector const &B) {
PreMaxA.clear();
PreMinA.clear();
SufMaxA.clear();
SufMinA.clear();
PreMaxB.clear();
PreMinB.clear();
SufMaxB.clear();
SufMinB.clear();
PreMaxA.resize(N + 1);
PreMinA.resize(N + 1);
SufMaxA.resize(N + 1);
SufMinA.resize(N + 1);
PreMaxB.resize(M + 1);
PreMinB.resize(M + 1);
SufMaxB.resize(M + 1);
SufMinB.resize(M + 1);
if (A[1] >= B[1])
return false;
PreMaxA[1] = ValuePair(A[1], 1);
PreMinA[1] = ValuePair(A[1], 1);
for (valueType i = 2; i <= N; ++i) {
PreMaxA[i] = std::max(PreMaxA[i - 1], ValuePair(A[i], i));
PreMinA[i] = std::min(PreMinA[i - 1], ValuePair(A[i], i));
}
SufMaxA[N] = ValuePair(A[N], N);
SufMinA[N] = ValuePair(A[N], N);
for (valueType i = N - 1; i >= 1; --i) {
SufMaxA[i] = std::max(SufMaxA[i + 1], ValuePair(A[i], i));
SufMinA[i] = std::min(SufMinA[i + 1], ValuePair(A[i], i));
}
PreMaxB[1] = ValuePair(B[1], 1);
PreMinB[1] = ValuePair(B[1], 1);
for (valueType i = 2; i <= M; ++i) {
PreMaxB[i] = std::max(PreMaxB[i - 1], ValuePair(B[i], i));
PreMinB[i] = std::min(PreMinB[i - 1], ValuePair(B[i], i));
}
SufMaxB[M] = ValuePair(B[M], M);
SufMinB[M] = ValuePair(B[M], M);
for (valueType i = M - 1; i >= 1; --i) {
SufMaxB[i] = std::max(SufMaxB[i + 1], ValuePair(B[i], i));
SufMinB[i] = std::min(SufMinB[i + 1], ValuePair(B[i], i));
}
auto const &[MinA, MinA_Pos] = PreMinA[N];
auto const &[MaxA, MaxA_Pos] = PreMaxA[N];
auto const &[MinB, MinB_Pos] = PreMinB[M];
auto const &[MaxB, MaxB_Pos] = PreMaxB[M];
if (MinA >= MinB || MaxA >= MaxB)
return false;
return checkUp(MinA_Pos, MaxB_Pos, N, M) && checkDown(MinA_Pos, MaxB_Pos, N, M);
}
}// namespace MAIN
using MAIN::solve;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
// #ifndef LOCAL_STDIO
// freopen("expand.in", "r", stdin);
// freopen("expand.out", "w", stdout);
// #endif
valueType ID, N, M, Q;
std::cin >> ID >> N >> M >> Q;
ValueVector A(N + 1), B(M + 1);
for (valueType i = 1; i <= N; ++i)
std::cin >> A[i];
for (valueType i = 1; i <= M; ++i)
std::cin >> B[i];
std::cout << ((solve(N, M, A, B) || solve(M, N, B, A)) ? 1 : 0);
for (valueType query = 0; query < Q; ++query) {
valueType KA, KB;
因此对于点
,向 和点 连一条容量为
的边即可。
Code
[NOIP2023] 三值逻辑
[NOIP2023] 双序列拓展
[SDOI2016] 数字配对
[HNOI2013] 切糕
__EOF__
本文作者: User-Unauthorized
本文链接: https://www.cnblogs.com/User-Unauthorized/p/2023-12-4.html
关于博主: 评论和私信会在第一时间回复。或者直接私信我。
版权声明: 本博客所 std::cin >> KA >> KB;
ValueVector TA = A, TB = B;
for (valueType i = 0; i < KA; ++i) {
valueType x, y;
std::cin >> x >> y;
TA[x] = y;
}
for (valueType i = 0; i < KB; ++i) {
valueType x, y;
std::cin >> x >> y;
TB[x] = y;
}
std::cout << ((solve(N, M, TA, TB) || solve(M, N, TB, TA)) ? 1 : 0);
}
std::cout << std::endl;
return 0;
}
[SDOI2016] 数字配对
对于每个数 \(i\),设 \(f_i\) 表示 \(a_i\) 的所有质因子的次数之和。那么两个数 \(i, j\) 可以配对当且仅当 \(a_i \mid a_j \land f_j = f_i + 1\) 或 \(a_j \mid a_i \land f_i = f_j + 1\)。
所以 \(f_i\) 奇偶性相同的数之间一定不可能配对,考虑将 \(f_i\) 为奇数的点作为左部点,\(f_i\) 为偶数的点作为右部点,在可以配对的数对之间连边,可以发现形成的网络为一个二分图。
那么接下来问题转化为了如何在获得的价值总和不小于 \(0\) 的前提下求出最大流。
考虑应用最大费用最大流,因为其均为剩余网络中的最长路,因此每轮增广后剩余网络中的最长路单调递减。
因此当前轮增广后的费用为负时退掉一些流使得费用为正后退出即可。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
class MCMF {
private:
struct Edge {
public:
typedef std::list<Edge> container;
typedef container::iterator iterator;
valueType to;
valueType cap;
valueType flow;
valueType cost;
iterator pair;
Edge() : to(-1), cap(-1), flow(-1), cost(0), pair(){};
Edge(valueType to, valueType cap, valueType c, iterator pair = iterator()) : to(to), cap(cap), flow(0), cost(c),
pair(pair){};
};
typedef std::vector<Edge::container> Graph;
typedef std::vector<Edge::iterator> IterVector;
typedef std::vector<bool> bitset;
valueType N;
Graph G;
ValueVector dist;
IterVector start;
bool Initialized;
public:
explicit MCMF(valueType N) : N(N), G(N + 1), dist(N + 1, 0), start(N + 1), Initialized(false){};
Edge::iterator addEdge(valueType from, valueType to, valueType cap, valueType cost) {
if (__builtin_expect(Initialized, false))
throw std::runtime_error("MCMF: addEdge after init");
G[from].emplace_back(to, cap, cost);
G[to].emplace_back(from, 0, -cost);
G[from].back().pair = std::prev(G[to].end());
G[to].back().pair = std::prev(G[from].end());
return std::prev(G[from].end());
}
void init() {
if (__builtin_expect(Initialized, false))
throw std::runtime_error("MCMF: init twice");
Initialized = true;
std::fill(dist.begin(), dist.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("MCMF: reset before init");
for (valueType i = 1; i <= N; ++i)
for (auto &iter : G[i])
iter.flow = 0;
std::fill(dist.begin(), dist.end(), 0);
Initialized = false;
}
std::pair<valueType, valueType> maxFlow(valueType S, valueType T) {
if (__builtin_expect(!Initialized, false))
throw std::runtime_error("MCMF: maxFlow before init");
std::pair<valueType, valueType> result(0, 0);
while (bfs(S, T)) {
IterVector begin = start;
bitset visited(N + 1, false);
valueType const flow = dfs(S, T, std::numeric_limits<valueType>::max(), begin, visited);
if (result.second + flow * dist[T] < 0) {
result.first += -result.second / dist[T];
result.second += result.second / dist[T] * dist[T];
break;
}
result.first += flow;
result.second += flow * dist[T];
}
return result;
}
private:
bool bfs(valueType S, valueType T) {
std::fill(dist.begin(), dist.end(), std::numeric_limits<valueType>::min() >> 5);
bitset visited(N + 1, false);
std::queue<valueType> Q;
Q.push(S);
dist[S] = 0;
visited[S] = true;
while (!Q.empty()) {
valueType const u = Q.front();
visited[u] = false;
Q.pop();
for (auto const &e : G[u]) {
if ((e.cap > e.flow) && (dist[e.to] < dist[u] + e.cost)) {
dist[e.to] = dist[u] + e.cost;
if (!visited[e.to]) {
Q.push(e.to);
visited[e.to] = true;
}
}
}
}
return dist[T] != (std::numeric_limits<valueType>::min() >> 5);
}
valueType dfs(valueType u, valueType T, valueType flow, IterVector &Begin, bitset &visited) {
if (u == T || flow == 0)
return flow;
valueType result = 0;
visited[u] = true;
for (auto &e = Begin[u]; e != G[u].end() && flow > 0; ++e) {
if (!visited[e->to] && e->cap > e->flow && dist[e->to] == dist[u] + e->cost) {
valueType const f = dfs(e->to, T, std::min(flow - result, e->cap - e->flow), Begin, visited);
if (f == 0) {
dist[e->to] = std::numeric_limits<valueType>::min();
continue;
}
e->flow += f;
e->pair->flow -= f;
result += f;
if (result == flow) {
visited[u] = false;
return flow;
}
}
}
visited[u] = false;
return result;
}
};
constexpr valueType INF = std::numeric_limits<valueType>::max();
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N;
std::cin >> N;
valueType const S = N + 1, T = N + 2;
MCMF mcmf(N + 3);
ValueVector A(N + 1), B(N + 1), C(N + 1), count(N + 1, 0);
for (valueType i = 1; i <= N; ++i)
std::cin >> A[i];
for (valueType i = 1; i <= N; ++i)
std::cin >> B[i];
for (valueType i = 1; i <= N; ++i)
std::cin >> C[i];
for (valueType i = 1; i <= N; ++i) {
valueType x = A[i];
for (valueType j = 2; j * j <= x; ++j) {
while (x % j == 0) {
x /= j;
++count[i];
}
}
if (x > 1)
++count[i];
}
for (valueType i = 1; i <= N; ++i) {
if ((count[i] & 1) == 1)
mcmf.addEdge(S, i, B[i], 0);
else
mcmf.addEdge(i, T, B[i], 0);
}
for (valueType i = 1; i <= N; ++i) {
if ((count[i] & 1) == 0)
continue;
for (valueType j = 1; j <= N; ++j)
if ((A[i] % A[j] == 0 && count[i] == count[j] + 1) || (A[j] % A[i] == 0 && count[j] == count[i] + 1))
mcmf.addEdge(i, j, INF, C[i] * C[j]);
}
mcmf.init();
auto const ans = mcmf.maxFlow(S, T);
std::cout << ans.first << std::endl;
return 0;
}
[HNOI2013] 切糕
可以发现切面会把点分为两个集合,故考虑最小割。
发现可以从点 \(\left(x, y, z\right)\) 向 \(\left(x, y, z + 1\right)\) 连边,流量为 \(v\left(x, y, z\right)\)。其中 \(\left(x, y, 0\right) = S, v\left(x, y, 0\right) = \infty, \left(x, y, R + 1\right) = T\)。
那么可以发现 \(S\) 集合意味着切面及以下的点的集合,\(T\) 集合意味着切面以上的点的集合,考虑如何处理相邻切面距离不能超过 \(D\) 的限制。
发现对于相邻的竖柱 \(a, b\),设 \(\left(a, x\right)\) 表示竖柱 \(a\) 上的第 \(x\) 层对应的节点。那么限制可以表示为
因此对于点 \(\left(x, y, z\right)\),向 \(\left(x \pm 1, y, z - D\right)\) 和点 \(\left(x, y \pm 1, z - D\right)\) 连一条容量为 \(\infty\) 的边即可。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::vector<ValueMatrix> ValueCube;
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;
}
valueType 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;
result += dfs(S, T, std::numeric_limits<valueType>::max(), begin);
}
return result;
}
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;
}
};
constexpr valueType INF = std::numeric_limits<valueType>::max();
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType P, Q, R, D;
std::cin >> P >> Q >> R >> D;
valueType size = 0;
valueType const S = ++size, T = ++size;
ValueCube ID(P + 1, ValueMatrix(Q + 1, ValueVector(R + 2, 0))), W(P + 1, ValueMatrix(Q + 1, ValueVector(R + 1, INF)));
for (valueType i = 1; i <= P; ++i) {
for (valueType j = 1; j <= Q; ++j) {
for (valueType k = 1; k <= R; ++k)
ID[i][j][k] = ++size;
ID[i][j][0] = S;
ID[i][j][R + 1] = T;
}
}
for (valueType k = 1; k <= R; ++k)
for (valueType i = 1; i <= P; ++i)
for (valueType j = 1; j <= Q; ++j)
std::cin >> W[i][j][k];
Dinic dinic(size);
for (valueType i = 1; i <= P; ++i)
for (valueType j = 1; j <= Q; ++j)
for (valueType k = 0; k <= R; ++k)
dinic.addEdge(ID[i][j][k], ID[i][j][k + 1], W[i][j][k]);
for (valueType i = 1; i <= P; ++i) {
for (valueType j = 1; j <= Q; ++j) {
for (valueType k = D; k <= R; ++k) {
if (i > 1)
dinic.addEdge(ID[i][j][k], ID[i - 1][j][k - D], INF);
if (i < P)
dinic.addEdge(ID[i][j][k], ID[i + 1][j][k - D], INF);
if (j > 1)
dinic.addEdge(ID[i][j][k], ID[i][j - 1][k - D], INF);
if (j < Q)
dinic.addEdge(ID[i][j][k], ID[i][j + 1][k - D], INF);
}
}
}
dinic.init();
std::cout << dinic.maxFlow(S, T) << std::endl;
return 0;
}
[SCOI2007] 修车
首先考虑转化贡献式,最小化平均等待时间可以转化为最小化等待时间之和。设 \(c_i\),表示第 \(i\) 辆车被第 \(c_i\) 名工人维修,\(f_i\) 表示第 \(i\) 辆车是倒数第 \(f_i\) 辆被维修的(对于维修其的工人而言),那么等待时间之和为
将每名工人拆成 \(n\) 个点,分别代表修倒数第 \(i\) 辆车的工人,这样工人和车均为一次性的(工人只能修一辆车,车只能被修一次)。
那么就可以进行连边了,求最小费用最大流即可。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
class MCMF {
private:
struct Edge {
public:
typedef std::list<Edge> container;
typedef container::iterator iterator;
valueType to;
valueType cap;
valueType flow;
valueType cost;
iterator pair;
Edge() : to(-1), cap(-1), flow(-1), cost(0), pair(){};
Edge(valueType to, valueType cap, valueType c, iterator pair = iterator()) : to(to), cap(cap), flow(0), cost(c),
pair(pair){};
};
typedef std::vector<Edge::container> Graph;
typedef std::vector<Edge::iterator> IterVector;
typedef std::vector<bool> bitset;
valueType N;
Graph G;
ValueVector dist;
IterVector start;
bool Initialized;
public:
explicit MCMF(valueType N) : N(N), G(N + 1), dist(N + 1, 0), start(N + 1), Initialized(false){};
Edge::iterator addEdge(valueType from, valueType to, valueType cap, valueType cost) {
if (__builtin_expect(Initialized, false))
throw std::runtime_error("MCMF: addEdge after init");
G[from].emplace_back(to, cap, cost);
G[to].emplace_back(from, 0, -cost);
G[from].back().pair = std::prev(G[to].end());
G[to].back().pair = std::prev(G[from].end());
return std::prev(G[from].end());
}
void init() {
if (__builtin_expect(Initialized, false))
throw std::runtime_error("MCMF: init twice");
Initialized = true;
std::fill(dist.begin(), dist.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("MCMF: reset before init");
for (valueType i = 1; i <= N; ++i)
for (auto &iter : G[i])
iter.flow = 0;
std::fill(dist.begin(), dist.end(), 0);
Initialized = false;
}
std::pair<valueType, valueType> maxFlow(valueType S, valueType T) {
if (__builtin_expect(!Initialized, false))
throw std::runtime_error("MCMF: maxFlow before init");
std::pair<valueType, valueType> result(0, 0);
while (bfs(S, T)) {
IterVector begin = start;
bitset visited(N + 1, false);
valueType const flow = dfs(S, T, std::numeric_limits<valueType>::max(), begin, visited);
result.first += flow;
result.second += flow * dist[T];
}
return result;
}
private:
bool bfs(valueType S, valueType T) {
std::fill(dist.begin(), dist.end(), std::numeric_limits<valueType>::max());
bitset visited(N + 1, false);
std::queue<valueType> Q;
Q.push(S);
dist[S] = 0;
visited[S] = true;
while (!Q.empty()) {
valueType const u = Q.front();
visited[u] = false;
Q.pop();
for (auto const &e : G[u]) {
if ((e.cap > e.flow) && (dist[e.to] > dist[u] + e.cost)) {
dist[e.to] = dist[u] + e.cost;
if (!visited[e.to]) {
Q.push(e.to);
visited[e.to] = true;
}
}
}
}
return dist[T] != std::numeric_limits<valueType>::max();
}
valueType dfs(valueType u, valueType T, valueType flow, IterVector &Begin, bitset &visited) {
if (u == T || flow == 0)
return flow;
valueType result = 0;
visited[u] = true;
for (auto &e = Begin[u]; e != G[u].end() && flow > 0; ++e) {
if (!visited[e->to] && e->cap > e->flow && dist[e->to] == dist[u] + e->cost) {
valueType const f = dfs(e->to, T, std::min(flow - result, e->cap - e->flow), Begin, visited);
if (f == 0) {
dist[e->to] = std::numeric_limits<valueType>::max();
continue;
}
e->flow += f;
e->pair->flow -= f;
result += f;
if (result == flow) {
visited[u] = false;
return flow;
}
}
}
visited[u] = false;
return result;
}
};
constexpr valueType INF = std::numeric_limits<valueType>::max();
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N, M, size = 0;
std::cin >> M >> N;
valueType const S = ++size, T = ++size;
ValueMatrix people(M + 1, ValueVector(N + 1, 0)), cost(M + 1, ValueVector(N + 1, 0));
ValueVector car(N + 1, 0);
for (valueType i = 1; i <= N; ++i)
car[i] = ++size;
for (valueType i = 1; i <= M; ++i)
for (valueType j = 1; j <= N; ++j)
people[i][j] = ++size;
MCMF mcmf(size);
for (valueType j = 1; j <= N; ++j)
for (valueType i = 1; i <= M; ++i)
std::cin >> cost[i][j];
for (valueType i = 1; i <= N; ++i)
mcmf.addEdge(car[i], T, 1, 0);
for (valueType i = 1; i <= M; ++i) {
for (valueType j = 1; j <= N; ++j) {
mcmf.addEdge(S, people[i][j], 1, 0);
for (valueType k = 1; k <= N; ++k)
mcmf.addEdge(people[i][j], car[k], 1, cost[i][k] * j);
}
}
mcmf.init();
auto const result = mcmf.maxFlow(S, T);
std::cout << std::setprecision(2) << std::fixed << ((double)result.second / (double)N) << std::endl;
return 0;
}
[NOI2012] 美食节
转化同 [SCOI2007] 修车,由于 \(p = \sum p_i\) 很大,\(nmp\) 的边数无法接受,但是可以发现最终的图中需要的边数是 \(\mathcal{O}(p)\) 级别的。因此我们可以先将所有的厨师的最后一次做饭建出来,若一个厨师的点被使用了那么将代表下一次的点对应的边连接即可。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;
class MCMF {
public:
struct Edge {
public:
typedef std::list<Edge> container;
typedef container::iterator iterator;
valueType to;
valueType cap;
valueType flow;
valueType cost;
iterator pair;
Edge() : to(-1), cap(-1), flow(-1), cost(0), pair(){};
Edge(valueType to, valueType cap, valueType c, iterator pair = iterator()) : to(to), cap(cap), flow(0), cost(c),
pair(pair){};
};
typedef std::vector<Edge::container> Graph;
typedef std::vector<Edge::iterator> IterVector;
typedef std::vector<bool> bitset;
valueType N;
Graph G;
ValueVector dist;
IterVector start;
bool Initialized;
public:
explicit MCMF(valueType N) : N(N), G(N + 1), dist(N + 1, 0), start(N + 1), Initialized(false){};
Edge::iterator addEdge(valueType from, valueType to, valueType cap, valueType cost) {
// if (__builtin_expect(Initialized, false))
// throw std::runtime_error("MCMF: addEdge after init");
G[from].emplace_back(to, cap, cost);
G[to].emplace_back(from, 0, -cost);
G[from].back().pair = std::prev(G[to].end());
G[to].back().pair = std::prev(G[from].end());
return std::prev(G[from].end());
}
void init() {
// if (__builtin_expect(Initialized, false))
// throw std::runtime_error("MCMF: init twice");
Initialized = true;
std::fill(dist.begin(), dist.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("MCMF: reset before init");
for (valueType i = 1; i <= N; ++i)
for (auto &iter : G[i])
iter.flow = 0;
std::fill(dist.begin(), dist.end(), 0);
Initialized = false;
}
bool bfs(valueType S, valueType T) {
std::fill(dist.begin(), dist.end(), std::numeric_limits<valueType>::max());
bitset visited(N + 1, false);
std::queue<valueType> Q;
Q.push(S);
dist[S] = 0;
visited[S] = true;
while (!Q.empty()) {
valueType const u = Q.front();
visited[u] = false;
Q.pop();
for (auto const &e : G[u]) {
if ((e.cap > e.flow) && (dist[e.to] > dist[u] + e.cost)) {
dist[e.to] = dist[u] + e.cost;
if (!visited[e.to]) {
Q.push(e.to);
visited[e.to] = true;
}
}
}
}
return dist[T] != std::numeric_limits<valueType>::max();
}
valueType dfs(valueType u, valueType T, valueType flow, IterVector &Begin, bitset &visited) {
if (u == T || flow == 0)
return flow;
valueType result = 0;
visited[u] = true;
for (auto &e = Begin[u]; e != G[u].end() && flow > 0; ++e) {
if (!visited[e->to] && e->cap > e->flow && dist[e->to] == dist[u] + e->cost) {
valueType const f = dfs(e->to, T, std::min(flow - result, e->cap - e->flow), Begin, visited);
if (f == 0) {
dist[e->to] = std::numeric_limits<valueType>::max();
continue;
}
e->flow += f;
e->pair->flow -= f;
result += f;
if (result == flow) {
visited[u] = false;
return flow;
}
}
}
visited[u] = false;
return result;
}
};
constexpr valueType INF = std::numeric_limits<valueType>::max();
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N, M, size = 0;
std::cin >> N >> M;
if (M == 1) {
ValueVector A(N + 1, 0), B;
for (valueType i = 1; i <= N; ++i)
std::cin >> A[i];
valueType const P = std::accumulate(A.begin(), A.end(), (valueType) 0);
B.reserve(P);
for (valueType i = 1; i <= N; ++i) {
valueType x;
std::cin >> x;
B.insert(B.end(), A[i], x);
}
std::sort(B.begin(), B.end());
valueType ans = 0;
for (valueType i = 0; i < P; ++i)
ans += B[i] * (P - i);
std::cout << ans << std::endl;
return 0;
}
ValueVector count(N + 1, 0);
for (valueType i = 1; i <= N; ++i)
std::cin >> count[i];
valueType const P = std::accumulate(count.begin(), count.end(), (valueType) 0);
valueType const S = ++size, T = ++size;
ValueMatrix chef(M + 1, ValueVector(P + 1, 0)), cost(M + 1, ValueVector(N + 1, 0));
ValueVector meal(N + 1, 0);
for (valueType i = 1; i <= N; ++i)
meal[i] = ++size;
for (valueType i = 1; i <= M; ++i)
for (valueType j = 1; j <= P; ++j)
chef[i][j] = ++size;
MCMF mcmf(size);
PairVector belong(size + 1);
for (valueType i = 1; i <= M; ++i)
for (valueType j = 1; j <= P; ++j)
belong[chef[i][j]] = ValuePair(i, j);
for (valueType j = 1; j <= N; ++j)
for (valueType i = 1; i <= M; ++i)
std::cin >> cost[i][j];
ValueVector usage(M + 1, 1);
for (valueType i = 1; i <= N; ++i)
mcmf.addEdge(meal[i], T, count[i], 0);
for (valueType i = 1; i <= M; ++i) {
mcmf.addEdge(S, chef[i][1], 1, 0);
for (valueType k = 1; k <= N; ++k)
mcmf.addEdge(chef[i][1], meal[k], 1, cost[i][k]);
}
mcmf.init();
std::pair<valueType, valueType> result(0, 0);
while (mcmf.bfs(S, T)) {
MCMF::IterVector begin = mcmf.start;
MCMF::bitset visited(size + 1, false);
valueType const flow = mcmf.dfs(S, T, std::numeric_limits<valueType>::max(), begin, visited);
for (auto const &iter : mcmf.G[S]) {
if (iter.flow != iter.cap || belong[iter.to].second < usage[belong[iter.to].first])
continue;
valueType const id = belong[iter.to].first;
if (usage[id] == P)
break;
++usage[id];
mcmf.addEdge(S, chef[id][usage[id]], 1, 0);
for (valueType k = 1; k <= N; ++k)
mcmf.addEdge(chef[id][usage[id]], meal[k], 1, cost[id][k] * usage[id]);
mcmf.start[chef[id][usage[id]]] = mcmf.G[chef[id][usage[id]]].begin();
}
result.first += flow;
result.second += flow * mcmf.dist[T];
}
std::cout << result.second << std::endl;
return 0;
}