【bzoj1797】[Ahoi2009]Mincut 最小割 网络流最小割+Tarjan
题目描述
给定一张图,对于每一条边询问:(1)是否存在割断该边的s-t最小割 (2)是否所有s-t最小割都割断该边
输入
第一行有4个正整数,依次为N,M,s和t。第2行到第(M+1)行每行3个正 整数v,u,c表示v中转站到u中转站之间有单向道路相连,单向道路的起点是v, 终点是u,切断它的代价是c(1≤c≤100000)。 注意:两个中转站之间可能有多条道路直接相连。 同一行相邻两数之间可能有一个或多个空格。
输出
对每条单向边,按输入顺序,依次输出一行,包含两个非0即1的整数,分 别表示对问题一和问题二的回答(其中输出1表示是,输出0表示否)。 同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。
样例输入
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
样例输出
1 0
1 0
0 0
1 0
0 0
1 0
1 0
题解
网络流最小割+Tarjan
(貌似是某结论题?)
跑网络流最小割,然后在残量网络上跑Tarjan,缩点。
对于一条边满流的边x->y:
如果x与y所属的SCC不同,则该边可能出现在最小割上;
如果x与s所属的SCC相同且y与t所属的SCC相同,则该边一定出现在最小割上。
具体证明可以参考 hzwer's blog (既然是结论题就懒得证结论了233)
#include <queue> #include <cstdio> #include <cstring> #define N 4010 #define M 120010 using namespace std; queue<int> q; typedef long long ll; int head[N] , to[M] , id[M] , next[M] , cnt = 1 , s , t , dis[N] , deep[N] , low[N] , tot , vis[N] , ins[N] , sta[N] , top , bl[N] , num , ans[M >> 1]; ll val[M]; inline void add(int x , int y , ll z , int i) { to[++cnt] = y , val[cnt] = z , id[cnt] = i , next[cnt] = head[x] , head[x] = cnt; to[++cnt] = x , val[cnt] = 0 , id[cnt] = 0 , next[cnt] = head[y] , head[y] = cnt; } bool bfs() { int x , i; memset(dis , 0 , sizeof(dis)); while(!q.empty()) q.pop(); dis[s] = 1 , q.push(s); while(!q.empty()) { x = q.front() , q.pop(); for(i = head[x] ; i ; i = next[i]) { if(val[i] && !dis[to[i]]) { dis[to[i]] = dis[x] + 1; if(to[i] == t) return 1; q.push(to[i]); } } } return 0; } ll dinic(int x , ll low) { if(x == t) return low; ll temp = low , k; int i; for(i = head[x] ; i ; i = next[i]) { if(val[i] && dis[to[i]] == dis[x] + 1) { k = dinic(to[i] , min(temp , val[i])); if(!k) dis[to[i]] = 0; val[i] -= k , val[i ^ 1] += k; if(!(temp -= k)) break; } } return low - temp; } void tarjan(int x) { int i; deep[x] = low[x] = ++tot , vis[x] = ins[x] = 1 , sta[++top] = x; for(i = head[x] ; i ; i = next[i]) { if(val[i]) { if(!vis[to[i]]) tarjan(to[i]) , low[x] = min(low[x] , low[to[i]]); else if(ins[to[i]]) low[x] = min(low[x] , deep[to[i]]); } } if(deep[x] == low[x]) { int t; num ++ ; do { t = sta[top -- ]; bl[t] = num , ins[t] = 0; }while(t != x); } } int main() { int n , m , i , x , y; ll z; scanf("%d%d%d%d" , &n , &m , &s , &t); for(i = 1 ; i <= m ; i ++ ) scanf("%d%d%lld" , &x , &y , &z) , add(x , y , z , i); while(bfs()) dinic(s , 1ll << 62); for(i = 1 ; i <= n ; i ++ ) if(!vis[i]) tarjan(i); for(x = 1 ; x <= n ; x ++ ) for(i = head[x] ; i ; i = next[i]) if(id[i] && !val[i]) ans[id[i]] = (bl[x] != bl[to[i]]) + ((bl[x] == bl[s] && bl[to[i]] == bl[t]) << 1); for(i = 1 ; i <= m ; i ++ ) printf("%d %d\n" , ans[i] & 1 , ans[i] >> 1); return 0; }