BZOJ 1797 最小割
Description
A,B两个国家正在交战,其中A国的物资运输网中有\(N\)个中转站,\(M\)条单向道路。设其中第\(i\) (\(1 \le i \le M\))条道路连接了\(v_{i},u_{i}\)两个中转站,那么中转站\(v_{i}\)可以通过该道路到达\(u_{i}\)中转站,如果切断这条道路,需要代价\(c_{i}\)。现在B国想找出一个路径切断方案,使中转站\(s\)不能到达中转站\(t\),并且切断路径的代价之和最小。 小可可一眼就看出,这是一个求最小割的问题。但爱思考的小可可并不局限于此。现在他对每条单向道路提出两个问题: 问题一:是否存在一个最小代价路径切断方案,其中该道路被切断? 问题二:是否对任何一个最小代价路径切断方案,都有该道路被切断? 现在请你回答这两个问题。
Input
第一行有\(4\)个正整数,依次为\(N,M,s\)和\(t\)。第\(2\)行到第\((M+1)\)行每行\(3\)个正 整数\(v,u,c\)表示\(v\)中转站到\(u\)中转站之间有单向道路相连,单向道路的起点是\(v\), 终点是\(u\),切断它的代价是\(c(1 \le c \le 100000)\)。 注意:两个中转站之间可能有多条道路直接相连。 同一行相邻两数之间可能有一个或多个空格。
Output
对每条单向边,按输入顺序,依次输出一行,包含两个非\(0\)即\(1\)的整数,分别表示对问题一和问题二的回答(其中输出\(1\)表示是,输出\(0\)表示否)。 同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。
Sample Input
6 7 1 6
1 2 3
1 3 2
2 4 4
2 5 1
3 5 5
4 6 2
5 6 3
Sample Output
1 0
1 0
0 0
1 0
0 0
1 0
1 0
HINT
设第\((i+1)\)行输入的边为\(i\)号边,那么\(\lbrace 1,2 \rbrace,\lbrace 6,7 \rbrace,\lbrace 2,4,6 \rbrace\)是仅有的三个最小代价切割方案。它们的并是\(\lbrace 1,2,4,6,7 \rbrace\),交是 \(\emptyset\)。
测试数据规模如下表所示
数据编号 \(N\) \(M\) 数据编号 \(N\) \(M\)
\(1\) \(10\) \(50\) \(6\) \(1000\) \(20000\)
\(2\) \(20\) \(200\) \(7\) \(1000\) \(40000\)
\(3\) \(200\) \(2000\) \(8\) \(2000\) \(50000\)
\(4\) \(200\) \(2000\) \(9\) \(3000\) \(60000\)
\(5\) \(1000\) \(20000\) \(10\) \(4000\) \(60000\)
此题的做法十分巧妙。
首先求最小割,直接跑网络流。紧接着在残留网络上进行tarjan缩点。
然后,对每条边进行询问:若该边已经满流,且两个端点并未在同一个强联通分量内部,则该店最小割集中的边(若在以强联通分量的内部,割掉此边无影响);若两个端点一个在\(S\)集中,一个在\(T\)集中,则该边一定在最小割集中(割掉此边后\(S\)与\(T\)不联通)。
#include<cstring>
#include<iostream>
#include<queue>
#include<cstdio>
#include<cstdlib>
#include<stack>
using namespace std;
#define inf (1<<29)
#define maxn 4010
#define maxm 60010
int toit[maxm*2],side[maxn],nd[maxn],next[maxm*2];
int cap[maxm*2],cur[maxn],id[maxn],d[maxn],pre[maxn],cnt = 1;
int n,m,source,sink,tot,all,dfn[maxn],low[maxn]; bool in[maxn];
stack <int> S;
inline int read()
{
int x = 0,f = 1; char ch = getchar();
while(ch < '0' || ch > '9'){ if(ch == '-') f=-1; ch=getchar(); }
while(ch >= '0' && ch <= '9'){ x = x * 10 + ch - '0'; ch = getchar(); }
return x * f;
}
inline void add(int a,int b,int c)
{
next[++cnt] = side[a]; side[a] = cnt;
toit[cnt] = b; cap[cnt] = c;
}
inline void ins(int a,int b,int c) { add(a,b,c); add(b,a,0); }
inline void bfs()
{
queue <int> team; int now,i;
memcpy(cur,side,4*(n+4));
team.push(sink); d[sink] = 1; in[sink] = true;
while (!team.empty())
{
now = team.front(); team.pop();
nd[d[now]]++;
for (i = side[now];i;i = next[i])
if (cap[i^1] && !in[toit[i]])
in[toit[i]] = true,d[toit[i]] = d[now] + 1,team.push(toit[i]);
}
}
inline void isap()
{
bfs();
int res = 0,now = source,ca = inf,i;
while (d[source] <= n)
{
if (now == sink)
{
while (now != source)
{
cap[pre[now]] -= ca; cap[pre[now]^1] += ca;
now = toit[pre[now]^1];
}
res += ca;
ca = inf;
}
bool flag = false;
for (i = cur[now];i;i = next[i])
if (cap[i] && d[toit[i]] == d[now] - 1)
{
cur[now] = pre[toit[i]] = i;
ca = min(ca,cap[i]);
now = toit[i];
flag = true;
break;
}
if (flag) continue;
int arg = n;
if (!--nd[d[now]]) break;
for (i = side[now];i;i = next[i])
if (cap[i] && d[toit[i]] < arg) arg = d[toit[i]];
++nd[d[now] = arg + 1]; cur[now] = side[now];
if (now != source) now = toit[pre[now] ^ 1];
}
return;
}
inline void dfs(int now)
{
low[now] = dfn[now] = ++tot;
S.push(now);
for (int i = side[now];i;i = next[i])
if (cap[i] && !in[toit[i]])
{
if (!dfn[toit[i]]) dfs(toit[i]);
low[now] = min(low[now],low[toit[i]]);
}
if (low[now] == dfn[now])
{
++all;
while (S.top() != now) id[S.top()] = all,in[S.top()] = true,S.pop();
id[S.top()] = all; in[S.top()] = true; S.pop();
}
}
int main()
{
freopen("1797.in","r",stdin);
freopen("1797.out","w",stdout);
n = read(); m = read(); source = read(); sink = read();
while (m--)
{
int a = read(),b = read(),c = read();
ins(a,b,c);
}
isap();
memset(in,false,n+2);
for (int i = 1;i <= n;++i) if (!dfn[i]) dfs(i);
for (int i = 2;i <= cnt;i += 2)
{
if (!cap[i])
{
if (id[toit[i]] != id[toit[i^1]]) printf("1 ");
else printf("0 ");
if (id[toit[i]] == id[source] &&id[toit[i^1]] == id[sink]) putchar('1');
else if (id[toit[i]] == id[sink] &&id[toit[i^1]] == id[source]) putchar('1');
else putchar('0');
}
else printf("0 0");
putchar('\n');
}
fclose(stdin); fclose(stdout);
return 0;
}