P10698 [SNCPC2024] 最大流 题解

Description

给定一个 nn 个点 mm 条边的有向无环图,图中每条边的容量为 11。对点 11 以外的每个点 ii,设从点 11 到点 ii 的最大流为 fif_i,试求出 min{fi, k}\min\{f_i,\ k\}

在边容量为 11 的图上,一个从点 11 到点 ii 的流即为一条从点 11 到点 ii 的路径。如果从点 11 到点 ii 最多能同时有 fif_i 个不交的流(即没有一条边同时属于两个流),则我们认为点 11 到点 ii 的最大流是 fif_i

n105,m2×105,k50n\leq 10^5,m\leq 2\times 10^5,k\leq 50

Solution

首先这题很像这道题,但是问题从点不交变为边不交。

我们是无法直接套用原来那道题的做法的,考虑把原图的边看成点,点看成边。

具体地,对于每个原图上的点,将其入边和出边两两连一条边,权值随机。注意到现在图里面是没有合适的起点的,由于总流量不超过 kk,所以可以建 kk 个虚拟起点,这些起点与 11 的出边两两连边。

对于每条原图上的边 ii,维护 fi,jf_{i,j} 表示从第 jj 个虚拟起点走到第 ii 条边的权值乘积之和。那么一个点的答案就是它入边向量构成的线性基大小。

现在需要连的边可能会有 O(n2)O(n^2) 级别,不能过。

这里我们会发现对于一个原图上的点,其入边的向量中不在线性基里面的是不用加入转移的,因为这里是随机变量,对于一条出边的贡献实际上是从入边向量张成线性空间里面随机选一个。只考虑线性基里面的向量的贡献跟这个的意义是一样的,所以将不在线性基里的向量删掉即可。

时间复杂度:O((n+m)k2)O((n+m)k^2)

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 1e5 + 5, kMaxM = 2e5 + 5, kMaxK = 55, kMod = 998244353;

int n, m, k;
int u[kMaxM], v[kMaxM], deg[kMaxN], res[kMaxN];
std::vector<int> G[kMaxN], rG[kMaxN];
std::mt19937 rnd(std::random_device{}());
std::uniform_int_distribution<> gen(0, kMod - 1);

int qpow(int bs, int64_t idx = kMod - 2) {
int ret = 1;
for (; idx; idx >>= 1, bs = (int64_t)bs * bs % kMod)
if (idx & 1)
ret = (int64_t)ret * bs % kMod;
return ret;
}

inline int add(int x, int y) { return (x + y >= kMod ? x + y - kMod : x + y); }
inline int sub(int x, int y) { return (x >= y ? x - y : x - y + kMod); }
inline void inc(int &x, int y) { (x += y) >= kMod ? x -= kMod : x; }
inline void dec(int &x, int y) { (x -= y) < 0 ? x += kMod : x; }

struct Vector {
int a[kMaxK];
void clear() { std::fill_n(a + 1, k, 0); }
friend Vector operator *(Vector a, int v) {
for (int i = 1; i <= k; ++i) a.a[i] = 1ll * v * a.a[i] % kMod;
return a;
}
friend Vector operator +(Vector a, Vector b) {
for (int i = 1; i <= k; ++i) a.a[i] = add(a.a[i], b.a[i]);
return a;
}
friend Vector operator -(Vector a, Vector b) {
for (int i = 1; i <= k; ++i) a.a[i] = sub(a.a[i], b.a[i]);
return a;
}
} f[kMaxM];

struct Base {
int cnt;
Vector a[kMaxK];
void clear() {
cnt = 0;
for (int i = 1; i <= k; ++i) a[i].clear();
}
void ins(Vector x) {
++cnt;
for (int i = 1; i <= k; ++i) {
if (x.a[i]) {
if (!a[i].a[i]) return void(a[i] = x);
int d = 1ll * x.a[i] * qpow(a[i].a[i]) % kMod;
x = x - a[i] * d;
}
}
--cnt;
}
} base;

void topo() {
std::queue<int> q;
for (int i = 1; i <= n; ++i)
if (!deg[i])
q.emplace(i);
for (; !q.empty();) {
int u = q.front(); q.pop();
base.clear();
if (u == 1) {
for (int i = 1; i <= k; ++i) base.a[i].a[i] = 1;
} else {
for (auto i : rG[u]) base.ins(f[i]);
res[u] = base.cnt;
}
for (auto id : G[u]) {
int v = ::v[id];
for (int i = 1; i <= k; ++i) f[id] = f[id] + base.a[i] * gen(rnd);
if (!--deg[v]) q.emplace(v);
}
}
}

void dickdreamer() {
std::cin >> n >> m >> k;
for (int i = 1; i <= m; ++i) {
std::cin >> u[i] >> v[i];
G[u[i]].emplace_back(i), rG[v[i]].emplace_back(i);
++deg[v[i]];
}
topo();
for (int i = 2; i <= n; ++i) std::cout << res[i] << " \n"[i == n];
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

P10698 [SNCPC2024] 最大流 题解
https://sobaliuziao.github.io/2025/08/27/post/5a90cfd5.html
作者
Egg_laying_master
发布于
2025年8月27日
许可协议