[AHOI2009]最小割

题意

给定一个带权有向图,对每条边回答:
是否可能出现在最小割中,是否必定出现在最小割中
n<=4000,m<=60000

Solution

根据网络流最大流的性质,可得出结论:

对于最小割中两种边:

可行边:
1.必定满流
2.残量网络中两端不连通

必经边:
1.一定是可行边(有可行边的两条性质)
2.残量网络中两端分别联通S,T

可以先缩点,然后再用并查集判断

code

#include<bits/stdc++.h>
using namespace std;
#define re register
#define ll long long
#define get getchar()
#define in inline
in int read()
{
    int t=0; char ch=get;
    while(ch<'0' || ch>'9') ch=get;
    while(ch<='9' && ch>='0') t=t*10+ch-'0', ch=get;
    return t;
}
const int _=6010;
const int inf=0x3f3f3f3f;
int n,m,S,T,tot=1, h[_];
struct edge{
    int to,ne,w,fr;
}e[500010];
in void add(int x,int y,int z)
{
    e[++tot].ne=h[x], e[tot].to=y, e[tot].w=z, h[x]=tot, e[tot].fr=x;
    e[++tot].ne=h[y], e[tot].to=x, e[tot].w=0, h[y]=tot, e[tot].fr=y; 
}
int dis[_],cur[_];
in int dfs(int u,int flow)
{
    if(!flow || u==T) return flow;
    int used=0,d;
    for(re int i=cur[u];i;i=e[i].ne)
    {
        int v=e[i].to; cur[u]=i;
        if(e[i].w && dis[v]==dis[u]+1)
            if(d=dfs(v,min(flow-used,e[i].w)))
            {
                e[i].w-=d, e[i^1].w+=d, used+=d;
                if(used==flow) break;
            }
    }
    return used;
}
in int bfs()
{
    queue<int> q;
    memset(dis,0x3f,sizeof(dis));
    dis[S]=0; q.push(S);
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        cur[u]=h[u];
        for(re int i=h[u],v=e[i].to; i; i=e[i].ne, v=e[i].to)
            if(e[i].w && dis[v]==inf) dis[v]=dis[u]+1, q.push(v);
    }
    return dis[T]!=inf;
}
int ans;
in void dinic()
{   int t; while(bfs())while(t=dfs(S,inf))ans+=t; }
int dfn[_], low[_], vis[_], st[_], top, cnt, num,col[_];
in void tarjan(int u)
{
    dfn[u]=low[u]=++cnt;st[++top]=u; vis[u]=1;
    for(re int i=h[u];i;i=e[i].ne)
    {
        int v=e[i].to;
        if(!e[i].w) continue;
        if(!dfn[v]) {
            tarjan(v);
            low[u]=min(low[u], low[v]);
        }
        else if(vis[v]) low[u]=min(low[u], dfn[v]);
    }
    if(dfn[u]!=low[u]) return;
    ++num;
    while(top)
    {
        int x=st[top--];
        col[x]=num, vis[x]=0;
        if(x==u) break;
    }
}
int a[_][2];
in void work(int x,int id)
{
    queue<int> q; q.push(x); a[x][id]=1;
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        for(re int i=h[u];i;i=e[i].ne)
        {
            int v=e[i].to;
            if(!e[i^id].w || a[v][id]) continue;
            a[v][id]=1; q.push(v);
        }
    }
}
int main()
{
    n=read(), m=read(), S=read(), T=read();
    for(re int i=1;i<=m;i++)
    {
        int x=read(), y=read(), z=read();
        add(x,y,z);
    }
    dinic();
    for(re int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
    work(S,0), work(T,1);
    for(re int i=2;i<=1+2*m;i+=2)
    {
        int x=e[i].fr, y=e[i].to;
        if(!e[i].w&&col[x]!=col[y]) cout<<"1 ";
        else cout<<"0 ";
        if(a[x][0]&a[y][1]) cout<<"1\n";
        else cout<<0<<endl;
    }
}

上一篇:条件测试与if语句用法


下一篇:【二分图——染色法】AcWing 860. 染色法判定二分图