zoukankan      html  css  js  c++  java
  • 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 brace,lbrace 6,7 brace,lbrace 2,4,6 brace)是仅有的三个最小代价切割方案。它们的并是(lbrace 1,2,4,6,7 brace),交是 (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('
    ');
    	}
    	fclose(stdin); fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    Java:volatile 关键字的一点理解
    Java:准备学习的高级主题
    ASP.NET MVC:看 MVC 源码,学习:如何将 Area 中的 Controller 放到独立的程序集?
    Tomcat:基础安装和使用教程
    Javascript:看 Javascript 规范,学 this 引用,你会懂的。
    T4:T4 笔记 + Trait 示例
    Java:Java快速入门
    .NET:C#的匿名委托 和 Java的匿名局部内部类
    FAQ:如何修改领域模型?
    设计原则:对象之间的关系
  • 原文地址:https://www.cnblogs.com/mmlz/p/4325817.html
Copyright © 2011-2022 走看看