Description
在遥远的东方,有一个神秘的民族,自称Y族。他们世代居住在水面上,奉龙王为神。每逢重大庆典, Y族都
会在水面上举办盛大的祭祀活动。我们可以把Y族居住地水系看成一个由岔口和河道组成的网络。每条河道连接着
两个岔口,并且水在河道内按照一个固定的方向流动。显然,水系中不会有环流(下图描述一个环流的例子)。

由于人数众多的原因,Y族的祭祀活动会在多个岔口上同时举行。出于对龙王的尊重,这些祭祀地点的选择必
须非常慎重。准确地说,Y族人认为,如果水流可以从一个祭祀点流到另外一个祭祀点,那么祭祀就会失去它神圣
的意义。族长希望在保持祭祀神圣性的基础上,选择尽可能多的祭祀的地点。
第一行包含两个用空格隔开的整数N、M,分别表示岔口和河道的数目,岔口从1到N编号。接下来M行,每行包
含两个用空格隔开的整数u、v,描述一条连接岔口u和岔口v的河道,水流方向为自u向v。 N ≤ 100 M ≤ 1 000
Output
第一行包含一个整数K,表示最多能选取的祭祀点的个数。
4 4
1 2
3 4
3 2
4 2
Sample Output
2
HINT
在样例给出的水系中,不存在一种方法能够选择三个或者三个以上的祭祀点。包含两个祭祀点的测试点的方案有两种:
选择岔口1与岔口3(如样例输出第二行),选择岔口1与岔口4。
水流可以从任意岔口流至岔口2。如果在岔口2建立祭祀点,那么任意其他岔口都不能建立祭祀点
但是在最优的一种祭祀点的选取方案中我们可以建立两个祭祀点,所以岔口2不能建立祭祀点。对于其他岔口
至少存在一个最优方案选择该岔口为祭祀点,所以输出为1011。
Solution
先跑一遍floyd,求出一个f,f[i][j]=1当且仅当从i出发可以到j。
那么显然如果f[i][j]=1,i和j肯定要放弃一个,建立最小割模型:S到i流量为1,i到j流量为INF,j到T流量为1。那么对于S->i->j->T,我们要么割掉S->i表示放弃i,要么割掉j->T,表示放弃j,于是跑一遍最大流就是最小割,即最少放弃的点的个数。n-mincut就是答案。
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
| #include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int INF = 1009999999;
int getint() {
int r = 0, k = 1; char c = getchar();
for (; '0' > c || c > '9'; c = getchar()) if (c == '-') k = -1;
for (; '0' <= c && c <= '9'; c = getchar()) r = r * 10 - '0' + c;
return r * k;
}
struct edge_type {
int to, next, r;
} edge[500005];
int S, T, cnte = 1, h[500005];
int Q[500005];
void ins(int u, int v, int f) {
++cnte;
edge[cnte].to = v;
edge[cnte].r = f;
edge[cnte].next = h[u];
h[u] = cnte;
++cnte;
edge[cnte].to = u;
edge[cnte].r = 0;
edge[cnte].next = h[v];
h[v] = cnte;
}
int tail, head, d[500005], cur[500005];
bool bfs() {
tail = head = 0;
for (int i = S; i <= T; ++i) d[i] = -1;
Q[tail++] = S;
d[S] = 0;
int now;
while (head < tail) {
now = Q[head++];
for (int i = h[now]; i; i = edge[i].next) {
if (d[edge[i].to] == -1 && edge[i].r) {
d[edge[i].to] = d[now] + 1;
Q[tail++] = edge[i].to;
}
}
}
return d[T] != -1;
}
int dfs(int now, int a) {
if (T == now || a == 0) return a;
int ret = 0, f;
for (int &i = cur[now]; i; i = edge[i].next) {
if (d[edge[i].to] != d[now]+1) continue;
if (f = dfs(edge[i].to, min(a, edge[i].r))) {
a -= f;
ret += f;
edge[i].r -= f;
edge[i^1].r += f;
if (a == 0) break;
}
}
if (ret == 0) d[now] = -1;
return ret;
}
int Dinic() {
int ret = 0;
while (bfs()) {
for (int i = S; i <= T; ++i)
cur[i] = h[i];
ret += dfs(S, INF);
}
return ret;
}
int f[150][150];
int main() {
int n = getint();
int x, y, m = getint();
while (m--) {
x = getint(); y = getint();
f[x][y] = 1;
}
for (int k = 1; k <= n; ++k)
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
if (i != j && j != k && i != k)
f[i][j] = f[i][j] || (f[i][k] && f[k][j]);
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
if (f[i][j]) {
ins(i, j+n, INF);
}
S = 0; T = n * 2 + 1;
for (int i = 1; i <= n; ++i) {
ins(S, i, 1);
ins(i + n, T, 1);
}
int ans = Dinic();
printf("%d", n - ans);
return 0;
}
|