1797: [Ahoi2009]Mincut 最小割
Time Limit: 10 Sec Memory Limit: 162 MB
Submit: 1244 Solved: 512
Description
A,B两个国家正在交战,其中A国的物资运输网中有N个中转站,M条单向道路。设其中第i (1≤i≤M)条道路连接了vi,ui两个中转站,那么中转站vi可以通过该道路到达ui中转站,如果切断这条道路,需要代价ci。现在B国想找出一个路径切断方案,使中转站s不能到达中转站t,并且切断路径的代价之和最小。 小可可一眼就看出,这是一个求最小割的问题。但爱思考的小可可并不局限于此。现在他对每条单向道路提出两个问题: 问题一:是否存在一个最小代价路径切断方案,其中该道路被切断? 问题二:是否对任何一个最小代价路径切断方案,都有该道路被切断?
现在请你回答这两个问题。
Input
第一行有4个正整数,依次为N,M,s和t。第2行到第(M+1)行每行3个正 整数v,u,c表示v中转站到u中转站之间有单向道路相连,单向道路的起点是v, 终点是u,切断它的代价是c(1≤c≤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号边,那么{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
Source
求最小割的可行边和必须边。
最小割:
最小割是割掉几条边,使得s和t不连通,并且割掉边的流量之和最小。且最小割等于最大流。
现在来考虑本题的要求。
把每个边看成粗细不同的水管,管子的粗细表示流量的大小,那么一条路径上的最大流量就是由最细的管子决定的。
因此这个最细的管子是满流的。
感性的想,要用最小的代价使水无法从s流到t,把这些最细的管子割掉就好了。
因此可行边和必须边一定是满流的。
先看可行边:
如果割掉这条边,还能从这条边的from到达to,这样割一点意义都没有。所以可行边被割掉之后,(在残量网络中)一定不能从他的from到达to。
再看必须边:
如果在残量网络中,s能到达这条边的from,这条边的to能到达t,说明这条边是他所在的路径上流量最小的边(唯一的),那么一定要割掉这条边。
因此这道题我们只要在残量网络中跑一边tarjan(因为有反向边,所以有环),根据上述条件判断对应点是否在同一个强连通分量中即可。
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <queue> #define M 1000+5 #define inf 0x3f3f3f3f #include <stack> using namespace std; stack<int> S; int tot=1,scc_cnt,s,t,h[M],low[M],dfs_clock,dfn[M]; int scc[M],vt[M],v[M],d[M],cur[M],n,m; struct edge { int from,to,cap,flow,ne; }E[200005]; void Addedge(int from,int to,int cap) { E[++tot]=(edge){from,to,cap,0,h[from]}; h[from]=tot; E[++tot]=(edge){to,from,0,0,h[to]}; h[to]=tot; } bool bfs() { queue<int> q; for (int i=1;i<=n;i++) v[i]=0; v[s]=1; d[s]=0; q.push(s); while (!q.empty()) { int x=q.front(); q.pop(); for (int i=h[x];i;i=E[i].ne) { edge e=E[i]; if (!v[e.to]&&e.cap>e.flow) { v[e.to]=1; d[e.to]=d[x]+1; q.push(e.to); } } } return v[t]; } int dfs(int x,int a) { if (x==t||!a) return a; int flow=0; for (int &i=cur[x];i;i=E[i].ne) { edge &e=E[i]; if (d[e.to]!=d[x]+1) continue; int f=dfs(e.to,min(a,e.cap-e.flow)); if (f) { a-=f; e.flow+=f; E[i^1].flow-=f; flow+=f; if (!a) break; } } return flow; } void dinic() { while (bfs()) { for (int i=1;i<=n;i++) cur[i]=h[i]; dfs(s,inf); } } void tarjan(int x) { dfn[x]=low[x]=++dfs_clock; S.push(x); for (int i=h[x];i;i=E[i].ne) { edge e=E[i]; if (e.cap-e.flow<=0) continue; if (!dfn[e.to]) { tarjan(e.to); low[x]=min(low[x],low[e.to]); } else if (!scc[e.to]) low[x]=min(low[x],dfn[e.to]); } if (dfn[x]==low[x]) { scc_cnt++; while (!S.empty()) { int now=S.top(); S.pop(); scc[now]=scc_cnt; if (now==x) break; } } } int main() { scanf("%d%d%d%d",&n,&m,&s,&t); for (int i=1;i<=m;i++) { int x,y,c; scanf("%d%d%d",&x,&y,&c); Addedge(x,y,c); } dinic(); for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i); for (int i=2;i<=tot;i+=2) { edge e=E[i]; if (e.cap==e.flow&&scc[e.to]!=scc[e.from]) printf("1"); else printf("0"); if (e.cap==e.flow&&scc[e.from]==scc[s]&&scc[e.to]==scc[t]) printf(" 1\n"); else printf(" 0\n"); } return 0; }
感悟:
1.TLE是点数看错,数组开小