A,B两个国家正在交战,其中A国的物资运输网中有(N)个中转站,(M)条单向道路。设其中第(i (1≤i≤M))条道路连接了(v_i,u_i)两个中转站,那么中转站(v_i)可以通过该道路到达(u_i)中转站,如果切断这条道路,需要代价(c_i)。
现在B国想找出一个路径切断方案,使中转站(s)不能到达中转站(t),并且切断路径的代价之和最小。
小可可一眼就看出,这是一个求最小割的问题。但爱思考的小可可并不局限于此。现在他对每条单向道路提出两个问题:
- 问题一:是否存在一个最小代价路径切断方案,其中该道路被切断?
- 问题二:是否对任何一个最小代价路径切断方案,都有该道路被切断?
现在请你回答这两个问题。
最小割的可行边和必须边
然而做这道题的时候并不会这东西QAQ
首先我们考虑((u,v))是可行边需要满足的条件
- 满流
- 在残余网络u和v不连通
满流就不用说了,如果u和v连通的话那么这个图也是连通的,就不是最小割了
然后考虑((u,v))是必须边需要满足的条件
- 满流
- 在残余网络中u和起点s连通,v和终点t连通
我们思考第二个条件,如果u和s不连通或者v和t连通,那么在单独的路径上一定会存在一条可行边,这个割掉和((u,v))割掉是等效的,就不是必须边了
所以我们可以直接对残余网络做tarjan,可行边需要满足u和v不在同一个强连通分量,必须边满足u和s在同一个强连通分量,v和t在同一个强连通分量
Code
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 4000;
const int M = 6e4;
const int inf = 1e9;
using namespace std;
int id[M + 5],co_cnt,co[M + 5],n,m,s,t,dep[M + 5],cur[M + 5],head[M + 5],nxt[M * 2 + 5],q[M + 5],ans,edge_cnt = 1,U[M + 5],V[M + 5],dfn[M + 5],dfn_cnt,low[M + 5],stk[M + 5],top;
struct edges
{
int to,cost;
}edge[M * 2 + 5];
int dfs(int u,int flow)
{
if (u == t)
return flow;
int sum = 0;
for (int &i = cur[u];i;i = nxt[i])
{
int v = edge[i].to,w = edge[i].cost;
if (dep[v] == dep[u] + 1 && w)
{
int res = dfs(v,min(flow,w));
edge[i].cost -= res;
edge[i ^ 1].cost += res;
sum += res;
flow -= res;
if (!flow)
return sum;
}
}
if (!sum)
dep[u] = 0;
return sum;
}
int bfs()
{
for (int i = 1;i <= n;i++)
dep[i] = 0,cur[i] = head[i];
dep[s] = 1;
int l = 1,r = 0;
q[++r] = s;
while (l <= r)
{
int u = q[l++];
if (u == t)
return 1;
for (int i = head[u];i;i = nxt[i])
{
int v = edge[i].to,w = edge[i].cost;
if (!dep[v] && w)
{
dep[v] = dep[u] + 1;
q[++r] = v;
}
}
}
return 0;
}
void add_edge(int u,int v,int w)
{
edge[++edge_cnt] = (edges){v,w};
nxt[edge_cnt] = head[u];
head[u] = edge_cnt;
}
void tarjan(int u)
{
dfn[u] = low[u] = ++dfn_cnt;
stk[++top] = u;
for (int i = head[u];i;i = nxt[i])
{
int v = edge[i].to,w = edge[i].cost;
if (!w)
continue;
if (!dfn[v])
{
tarjan(v);
low[u] = min(low[u],low[v]);
}
else
if (!co[v])
low[u] = min(low[u],dfn[v]);
}
if (low[u] == dfn[u])
{
co[u] = ++co_cnt;
while (stk[top] != u)
co[stk[top--]] = co_cnt;
top--;
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&s,&t);
int w;
for (int i = 1;i <= m;i++)
{
scanf("%d%d%d",&U[i],&V[i],&w);
add_edge(U[i],V[i],w);
id[i] = edge_cnt;
add_edge(V[i],U[i],0);
}
while (bfs())
ans += dfs(s,inf);
for (int i = 1;i <= n;i++)
if (!dfn[i])
tarjan(i);
for (int i = 1;i <= m;i++)
{
if (!edge[id[i]].cost && co[U[i]] != co[V[i]])
printf("1 ");
else
printf("0 ");
if (!edge[id[i]].cost && co[U[i]] == co[s] && co[V[i]] == co[t])
printf("1 ");
else
printf("0 ");
putchar(10);
}
return 0;
}