zoukankan      html  css  js  c++  java
  • BZOJ1797 [Ahoi2009]Mincut 最小割 【最小割唯一性判定】

    题目

    A,B两个国家正在交战,其中A国的物资运输网中有N个中转站,M条单向道路。设其中第i (1≤i≤M)条道路连接了vi,ui两个中转站,那么中转站vi可以通过该道路到达ui中转站,如果切断这条道路,需要代价ci。现在B国想找出一个路径切断方案,使中转站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

    提示

    设第(i+1)行输入的边为i号边,那么{1,2},{6,7},{2,4,6}是仅有的三个最小代价切割方案。它们的并是{1,2,4,6,7},交是 。 【数据规模和约定】 测试数据规模如下表所示 数据编号 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

    2015.4.16新加数据一组,可能会卡掉从前可以过的程序。

    题解

    先跑最大流求出任意一个最小割
    对残量网络缩点
    然后对于一条满流的边(u,v)
    ①Scc[u]!=Scc[v],存在(u,v)被割的方案
    ②Scc[u]Scc[S]&&Scc[v]Scc[T],(u,v)必定被割

    jcvb:
    在残余网络上跑tarjan求出所有SCC,记id[u]为点u所在SCC的编号。显然有id[s]!=id[t](否则s到t有通路,能继续增广)。
    ①对于任意一条满流边(u,v),(u,v)能够出现在某个最小割集中,当且仅当id[u]!=id[v];
    ②对于任意一条满流边(u,v),(u,v)必定出现在最小割集中,当且仅当id[u]id[s]且id[v]id[t]。

    <将每个SCC缩成一个点,得到的新图就只含有满流边了。那么新图的任一s-t割都对应原图的某个最小割,从中任取一个把id[u]和id[v]割开的割即可证明。

    <
    :假设将(u,v)的边权增大,那么残余网络中会出现s->u->v->t的通路,从而能继续增广,于是最大流流量(也就是最小割容量)会增大。这即说明(u,v)是最小割集中必须出现的边。

    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #include<algorithm>
    #define LL long long int
    #define REP(i,n) for (int i = 1; i <= (n); i++)
    #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
    #define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<' '; puts("");
    using namespace std;
    const int maxn = 4005,maxm = 200005,INF = 1000000000;
    inline int read(){
    	int out = 0,flag = 1; char c = getchar();
    	while (c < 48 || c > 57) {if (c == '-') flag = -1; c = getchar();}
    	while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - '0'; c = getchar();}
    	return out * flag;
    }
    int h[maxn],ne = 2,n,m,S,T;
    struct EDGE{int from,to,nxt,f;}ed[maxm];
    inline void build(int u,int v,int w){
    	ed[ne] = (EDGE){u,v,h[u],w}; h[u] = ne++;
    	ed[ne] = (EDGE){v,u,h[v],0}; h[v] = ne++;
    }
    int vis[maxn],d[maxn],cur[maxn];
    bool bfs(){
    	memset(d,0,sizeof(d));
    	memset(vis,0,sizeof(vis));
    	queue<int> q;
    	vis[S] = true; q.push(S); int u;
    	while (!q.empty()){
    		u = q.front(); q.pop();
    		Redge(u) if (ed[k].f && !vis[to = ed[k].to]){
    			d[to] = d[u] + 1; vis[to] = true; q.push(to);
    		}
    	}
    	return vis[T];
    }
    int dfs(int u,int minf){
    	if (u == T || !minf) return minf;
    	int f,flow = 0,to;
    	if (cur[u] == -1) cur[u] = h[u];
    	for (int& k = cur[u]; k; k = ed[k].nxt)
    		if (d[to = ed[k].to] == d[u] + 1 && (f = dfs(to,min(ed[k].f,minf)))){
    			ed[k].f -= f; ed[k ^ 1].f += f;
    			flow += f; minf -= f;
    			if (!minf) break;
    		}
    	return flow;
    }
    int maxflow(){
    	int flow = 0;
    	while (bfs()){
    		for (int i = 1; i <= n; i++) cur[i] = -1;
    		flow += dfs(S,INF);
    	}
    	return flow;
    }
    int dfn[maxn],low[maxn],st[maxn],Scc[maxn],top,cnt,scci;
    void dfs(int u){
    	dfn[u] = low[u] = ++cnt;
    	st[++top] = u;
    	Redge(u) if (ed[k].f){
    		if (!dfn[to = ed[k].to]){
    			dfs(to);
    			low[u] = min(low[u],low[to]);
    		}else if (!Scc[to]) low[u] = min(low[u],dfn[to]);
    	}
    	if (dfn[u] == low[u]){
    		scci++;
    		do{
    			Scc[st[top]] = scci;
    		}while (st[top--] != u);
    	}
    }
    int ans[2][maxm];
    int main(){
    	n = read(); m = read(); S = read(); T = read(); int a,b,w;
    	REP(i,m){
    		a = read(); b = read(); w = read();
    		build(a,b,w);
    	}
    	maxflow();
    	REP(i,n) if (!dfn[i]) dfs(i);
    	for (int i = 1; i <= m; i++){
    		int k = i << 1;
    		if (!ed[k].f){
    			if (Scc[ed[k].from] != Scc[ed[k].to]) ans[0][i] = 1;
    			if (Scc[ed[k].from] == Scc[S] && Scc[ed[k].to] == Scc[T]) ans[1][i] = 1;
    		}
    	}
    	for (int i = 1; i <= m; i++) printf("%d %d
    ",ans[0][i],ans[1][i]);
    	return 0;
    }
    
    
  • 相关阅读:
    所有蚂蚁掉下来前的最后一刻
    最长有效括号
    n的第k个因子--leetcode1492
    删掉一个元素以后全为1的最长子数组。
    public class和class的区别
    数据库中的乐观锁和悲观锁以及实现方式
    HashMap底层实现原理 扩容机制
    jvm 内存泄漏现象和处理方案
    Django模板标签语法
    Django图片的上传与下载
  • 原文地址:https://www.cnblogs.com/Mychael/p/8322074.html
Copyright © 2011-2022 走看看