[AHOI2009]最小割

题目

最小割的可行边和必须边

可行边\((u,v)\)需要满足以下两个条件

  1. 满流

  2. 残量网络中不存在\(u\)\(v\)的路径

这个挺好理解的呀,如果存在还存在路径的话那么这条边就不会是瓶颈了

必须边\((u,v)\)需要满足的条件

  1. 满流

  2. 残量网络中\(S\)能到达\(u\)\(v\)能到达\(T\)

这样的话\((u,v)\)就成为了唯一的瓶颈了

我们可以直接在残量网络上跑\(tarjan\),只跑没满流的边

如果发现\(u\)\(v\)不在同一强联通分量里,就说明这是一条可行边

因为\((u,v)\)满流,\((v,u)\)必然存在,在同一连通分量里就说明可以从\(u\)走到\(v\)形成一个环,也就存在\(u\)\(v\)的路径

如果\(u\)\(S\)在同一个强联通分量里,\(v\)\(T\)在同一个强连通分量里,那么说明这是一条必须边,和上面类似

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
const int maxn=4e3+5;
const int inf=1e9;
inline int read() {
    char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
    while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
std::queue<int> q;
struct E{int v,nxt,f;}e[150000];
int n,m,S,T,cnt,top,p,mid,num=1;
int dfn[maxn],low[maxn],st[maxn],f[maxn];
int head[maxn],d[maxn],cur[maxn],col[maxn];
inline void C(int x,int y,int f) {
    e[++num].v=y;e[num].nxt=head[x];
    head[x]=num;e[num].f=f;
}
int X[60005],Y[60005],id[60005];
inline void add(int x,int y,int f) {C(x,y,f),C(y,x,0);}
inline int BFS() {
    for(re int i=1;i<=n;i++) d[i]=0,cur[i]=head[i];
    d[S]=1,q.push(S);
    while(!q.empty()) {
        int k=q.front();q.pop();
        for(re int i=head[k];i;i=e[i].nxt)
        if(!d[e[i].v]&&e[i].f) d[e[i].v]=d[k]+1,q.push(e[i].v);
    }
    return d[T];
}
int dfs(int x,int now) {
    if(x==T||!now) return now;
    int flow=0,ff;
    for(re int& i=cur[x];i;i=e[i].nxt)
    if(d[e[i].v]==d[x]+1) {
        ff=dfs(e[i].v,min(e[i].f,now));
        if(now<=0) continue;
        now-=ff,flow+=ff,e[i].f-=ff,e[i^1].f+=ff;
        if(!now) break;
    }
    return flow;
}
void tarjan(int x) {
    dfn[x]=low[x]=++cnt;
    st[++top]=x;f[x]=1;
    for(re int i=head[x];i;i=e[i].nxt) {
        if(!e[i].f) continue;
        if(!dfn[e[i].v]) tarjan(e[i].v),low[x]=min(low[x],low[e[i].v]);
        else if(f[e[i].v]) low[x]=min(low[x],dfn[e[i].v]);
    }
    if(dfn[x]==low[x]) {
        ++p;
        do {
            mid=st[top--];
            f[mid]=0;
            col[mid]=p;
        }while(x!=mid);
    }
}
int main() {
    n=read(),m=read();S=read(),T=read();
    for(re int z,i=1;i<=m;i++) {
        X[i]=read(),Y[i]=read();
        z=read();id[i]=num+1;add(X[i],Y[i],z);
    }
    while(BFS()) dfs(S,inf);
    for(re int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    for(re int i=1;i<=m;i++) {
        if(col[X[i]]!=col[Y[i]]&&!e[id[i]].f) putchar('1');
            else putchar('0');
        putchar(' ');
        if(!e[id[i]].f&&col[X[i]]==col[S]&&col[Y[i]]==col[T]) putchar('1');
            else putchar('0');
        putchar(10);
    }
    return 0;
}

转载于:https://www.cnblogs.com/asuldb/p/10608552.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值