抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

闲话

基于本题没有题解,那我来水一发网络流,先声明一下,本篇题解可能有部分口胡现象(本人太菜不会证明,但结论是对的,感性理解一下)。

正文

本题的结果不能直接用网络流跑出来(至少我不能)。网络流的题重在建图,但这题不一样,他更多是在考察对残余网络的操作。

首先我们看这个东西和二分图匹配有点像所以先把图拆点然后画出来,如下。

显然这个图的最大流(最大匹配)是 ,即最多能选出三对组队的人,而选用后会对最大流造成影响的边是 。因为选了这几条边后会直接或间接占用其他的流量通道,所以在这个图中答案显然是上述几条边,但如果我们看下面这个图。

能删去的边就不是那三条了,而只有 ,两条,相比之下 这条边就不会造成影响了,因为 点可以通过选择 点原来必须经过的点,即可以走 这条边。

然后我们发现可以将所有的便分为三种:

  1. 必行边:要保证最大流不变的情况下必须流过的边。
  2. 可行边:要保证最大流不变情况下有选择余地的边,即要在若干条边中选择一条边作为要流过的边。
  3. 不行边:要保证最大流不变情况下不能有流量流过的边。

那么可以发现,在第一个图中, ,都是必行边,而其他三条都为不行边,而第二个图中,只有 是必行边,而 ,都是可行边。

对于任意一条用于匹配的边,经过观察可以发现(下文的强连通分量指在残余网络中求得的结果,即求强连通分量的算法在运行时只能跑有流量的边):

  1. 若该边流量为一,且该边的两个点不位于一个强连通分量,该边为必行边。
  2. 若该边的两点位于同一强连通分量中,且该强连通分量中存在一条从左点指向右点的边,即该强连通分量中有已经匹配成功的点,该边为可行边。
  3. 若该边流量为零且两点并不位于同一强连通分量,该边为不行边。

接下来讲一下为什么可行边的定义如上,先看图。

首先图中四个点位于一个强连通分量(网络流要求双向建边),我们由强连通分量的定义,这四个点最后会位于一个可以互相到达的环中,而因为这个性质,在这个环中,最大流相当于选择若干条边刚好将整个环的点覆盖,是可以自由选择的。

可行边理解了,必行边和不行边的定义应该也明白了。

那整道题的思路也清晰了,在原二分图中跑最大流,然后在有流量的边上跑强连通分量,最后判定不行边。

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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/*
g++ -o2 P10940.cpp -o c -std=c++14
.\c

*/

#include<cstring>
#include<cstdio>

using namespace std;
const int N=2e4+100;
const int M=4e5+100;
const int inf=0x3f3f3f3f;

char *p1,*p2;
char buf[100];

// #define nc() getchar()
#define nc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,100,stdin),p1==p2)?EOF:*p1++)

inline void read(int &x){
x=0;
char ch=nc();
while(ch<48 || ch>57){
ch=nc();
}
while(ch>=48 && ch<=57){
x=(x<<3)+(x<<1)+ch-48;
ch=nc();
}
return ;
}

int tot;
int head[N];
struct edge{
// int x;
int y;
int f;
int next;
}e[M];

int dep[N];
int gap[N];
int cur[N];

int last;
int dfn[N];
int low[N];

int cidx;
int col[N];

int nl,nr;
int n,m;
int s,t;

int bri_sta;
int top;
int pri[N];

int q[M];
int stk[M];
int _top;

inline int max(int a,int b){return a>b?a:b;}
inline int min(int a,int b){return a<b?a:b;}
inline int ops(int x){return x^1;}

void init(){
tot=0;
memset(head,-1,sizeof(head));
s=0;
t=nl+nr+1;
return ;
}

inline void add(int x,int y,int f){
e[tot].y=y;
e[tot].f=f;
e[tot].next=head[x];
head[x]=tot++;
return ;
}

inline void make(int x,int y,int f){
add(x,y,f);
add(y,x,0);
return ;
}

void debug(){
for(int i=0;i<tot;i+=2){
int x=e[ops(i)].y;
int y=e[i].y;
int f=e[i].f;
printf("%d %d %d \n",x,y,f);
}
return ;
}

void bfs(int start,int to){
memset(dep,-1,sizeof(dep));
int hh=0;
int tt=1;
q[hh]=to;
dep[to]=0;
gap[0]=1;
while(hh!=tt){
int x=q[hh++];
if(hh==M)hh=0;
for(int i=head[x];~i;i=e[i].next){
int y=e[i].y;
if(dep[y]!=-1)continue;
dep[y]=dep[x]+1;
gap[dep[y]]++;
q[tt++]=y;
if(tt==M)tt=0;
}
}
return ;
}

int find(int x,int to,int lim){
if(x==to)return lim;
int flow=0;
for(int i=cur[x];~i;i=e[i].next){
int y=e[i].y;
int f=e[i].f;
cur[x]=i;
if(!f)continue;
if(dep[y]+1==dep[x]){
int t=find(y,to,min(f,lim-flow));
if(t>0){
e[i].f-=t;
e[ops(i)].f+=t;
flow+=t;
}
if(flow==lim)return flow;
}
}
gap[dep[x]]--;
if(!gap[dep[x]])dep[s]=n+1;
dep[x]++;
gap[dep[x]]++;
return flow;
}

int ISAP(int start,int to){
int flow=0;
bfs(start,to);
while(dep[start]<=n+1){
memcpy(cur,head,sizeof(head));
// for(int i=1;i<=n+1000;i++)cur[i]=head[i];
flow+=find(start,to,inf);
}
return flow;
}

void tarjan(int x){
last++;
dfn[x]=last;
low[x]=last;
stk[++_top]=x;
for(int i=head[x];~i;i=e[i].next){
if(!e[i].f)continue;
int y=e[i].y;
if(!dfn[y]){
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(!col[y])low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x]){
cidx++;
while(_top){
int t=stk[_top--];
col[t]=cidx;
if(t==x)break;
}
}
return ;
}

int main(){
read(nl);
read(nr);
read(m);
n=nl+nr+2;
init();
for(int i=1;i<=nl;i++)make(s,i,1);
for(int i=1;i<=nr;i++)make(i+nl,t,1);
bri_sta=tot;
for(int i=1;i<=m;i++){
int x,y;
read(x);
read(y);
y+=nl;
make(x,y,1);
}
// debug();
// printf("%d\n",ISAP(s,t));
int ans=ISAP(s,t);
for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
for(int i=bri_sta;i<tot;i+=2){
int x=e[ops(i)].y;
int y=e[i].y;
int f=e[i].f;
if(f && col[x]!=col[y]){
pri[++top]=(i-bri_sta)/2+1;
}
}
printf("%d\n",top);
for(int i=1;i<=top;i++){
printf("%d ",pri[i]);
}
putchar('\n');
return 0;
}