大概意思是让求一个图的最小割的必须边和可行边
然后做法就是在残量网络上跑tarjan缩点。如果这个边没有满流,它就没有被割,肯定不是。
如果这个边的from和to不在一个点,就是可行边。如果from和S在一起,to和T在一起,就是必须边。
jcvb:(策神Orz
在残余网络上跑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]。
#include <iostream> #include <cstdio> #include <queue> #include <cstring> using namespace std; int n,m,S,T,ecnt=1; const int M=120005,N=40005; struct Edge{ int from,to,nxt,val; }e[M]; int h[N],maxflow,head[N]; inline void add(int bg,int ed,int val) { e[++ecnt]=(Edge){bg,ed,head[bg],val};head[bg]=ecnt; } bool bfs() { queue<int>q; q.push(S); memset(h,-1,sizeof (int)*(n+4)); h[S]=0; while(!q.empty()) { int u=q.front();q.pop(); for(int i=head[u];i;i=e[i].nxt) { int v=e[i].to; if(h[v]==-1&&e[i].val) { h[v]=h[u]+1;q.push(v); } } } return h[T]!=-1; } int dfs(int x,int f) { if(x==T) return f; int used=0,tp; for(int i=head[x];i;i=e[i].nxt) { int v=e[i].to; if(e[i].val&&h[x]+1==h[v]) { tp=dfs(v,min(e[i].val,f-used)); used+=tp; e[i].val-=tp; e[i^1].val+=tp; if(used==f) return used; } } if(used==0) h[x]=-1; return used; } void dinic() {while(bfs())dfs(S,0x3f3f3f3f);} int stk[N],top,id[N],low[N],dfn[N],tim,tot; bool vis[N]; void tarjan(int x) { low[x]=dfn[x]=++tim;vis[x]=1;stk[++top]=x; for(int i=head[x];i;i=e[i].nxt) { int v=e[i].to; if(!e[i].val) continue; if(!dfn[v]) { tarjan(v); low[x]=min(low[x],low[v]); } else if(vis[v]) low[x]=min(low[x],dfn[v]); } if(dfn[x]==low[x]) { id[x]=++tot;vis[x]=0; while(stk[top]!=x) { id[stk[top]]=tot;vis[stk[top]]=0; top--; } top--; } } int main() { scanf("%d%d%d%d",&n,&m,&S,&T); for(int i=1,a,b,c;i<=m;i++) scanf("%d%d%d",&a,&b,&c),add(a,b,c),add(b,a,0); dinic(); for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i); for(int i=2;i<=ecnt;i+=2) { if(e[i].val) {puts("0 0");continue;} if(id[e[i].from]!=id[e[i].to]) putchar('1'); else {puts("0 0");continue;} putchar(' '); if(id[S]==id[e[i].from]&&id[T]==id[e[i].to]) puts("1");else puts("0"); } }