ZOJ 3496 Assignment | 二分+有上下界网络流

时间:2022-06-01 16:53:03

题目:

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3496

大概意思:给你一个网络,有源汇,在保证最大流的情况下求下面两个问题答案

1.所有边中流量最大的边流量最小

2.所有边中流量最小的边流量最大


题解:

De了一下午啊啊,之前学的上下界网络流有问题!

对于问题一,我们二分最大流量,每次建图跑最大流,看是不是和之前一样即可

对于问题二,我们同样二分答案lim,这样每条边满足流量限制w[i]∈[lim,c[i]]

这样建图跑有源汇最大流即可.

下面是坑点

之前写的有源汇最大流都是先跑可行流然后把超级源和超级汇删掉再跑,答案还得加加减减,但是就是Wa

后来发现其实并没有那么难,在可行流的残余网络直接再跑一边最大流就是答案

感性证明如下:

因为如果可行的话超级源的所有出边都满流,这样算最大流的时候只会算到t->s的反边上,又因为原来的可行流就在这条边上,这样直接就能算出答案

别忘了输出答案*p

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
typedef long long ll;
#define N 505
#define M 400005
#define INF 0x3f3f3f3f
using namespace std;
ll Case,n,m,t,s,P,ans1,ans2,S,T,ecnt,Maxflow,l,r,u[M],v[M],c[M],mid,Maxc,lim;
ll head[N],lev[N],cur[N],du[N];
queue <ll> q;
struct adj
{
    ll nxt,v,w;
} e[M];
ll read()
{
    ll ret=0,neg=1;
    char j=getchar();
    for (; j>'9' || j<'0'; j=getchar())
        if (j=='-') neg=-1;
    for (; j>='0' && j<='9'; j=getchar())
        ret=ret*10+j-'0';
    return ret*neg;
}
void add(ll u,ll v,ll w)
{
    e[++ecnt].v=v;e[ecnt].w=w;e[ecnt].nxt=head[u];head[u]=ecnt;
    e[++ecnt].v=u;e[ecnt].w=0;e[ecnt].nxt=head[v];head[v]=ecnt;
}
void init()
{
    ecnt=1;
    memset(head,0,sizeof(head));
}
bool Bfs()
{
    while (!q.empty()) q.pop();
    for (ll i=1; i<=lim; i++)
        cur[i]=head[i],lev[i]=-1;
    q.push(S),lev[S]=1;
    while (!q.empty())
    {
        ll u=q.front();
        q.pop();
        for (ll i=head[u],v; i; i=e[i].nxt)
            if (lev[v=e[i].v]==-1 && e[i].w>0)
            {
                q.push(v),lev[v]=lev[u]+1;
                if (v==T) return 1;
            }
    }
    return 0;
}
ll Dfs(ll u,ll flow)
{
    if (u==T) return flow;
    ll ret=0,delta;
    for (ll &i=cur[u],v; i; i=e[i].nxt)
        if (e[i].w>0 && lev[v=e[i].v]==lev[u]+1)
        {
            delta=Dfs(v,min(e[i].w,flow-ret));
            if (delta)
            {
                e[i].w-=delta;
                e[i^1].w+=delta;
                ret+=delta;
                if (ret==flow) break;
            }
        }
    return ret;
}
ll getG(ll lim)
{
    ll sum=0,tmp=0;
    init();
    add(t,s,INF);S=n+1,T=n+2;
    memset(du,0,sizeof(du));
    for (ll i=1; i<=m; i++)
        if (c[i]-lim<0) return -1;
        else add(u[i],v[i],c[i]-lim),du[u[i]]-=lim,du[v[i]]+=lim;
    for (ll i=1; i<=n; i++)
    {
        if (du[i]>0) add(S,i,du[i]),sum+=du[i];
        if (du[i]<0) add(i,T,-du[i]);
    }
    while (Bfs()) tmp+=Dfs(S,INF);
    if (tmp!=sum) return -1;
    tmp=0;S=s;T=t;
    while (Bfs()) tmp+=Dfs(S,INF);
    return tmp;
}
int main()
{
    Case=read();
    while (Case--)
    {
        n=read(),m=read(),s=read(),t=read(),P=read();
        init();
        S=++s;T=++t;
        Maxflow=Maxc=l=0;
        lim=n+2;
        for (ll i=1; i<=m; i++)
            u[i]=read(),v[i]=read(),c[i]=read(),add(++u[i],++v[i],c[i]),r=Maxc=max(Maxc,c[i]);
        while (Bfs()) Maxflow+=Dfs(S,INF);
        while (l<r)
        {
            init();
            ll mid=l+r>>1,tmp=0;
            for (ll i=1; i<=m; i++) add(u[i],v[i],min(c[i],mid));
            while (Bfs()) tmp+=Dfs(S,INF);
            if (tmp==Maxflow) r=mid;
            else l=mid+1;
        }
        ans1=l;l=0;r=Maxc;
        while (l<r)
        {
            ll mid=l+r+1>>1,tmp=0;
            if (getG(mid)==Maxflow) l=mid;
            else r=mid-1;
        }
        ans2=l;
        printf("%lld %lld\n",1ll*ans1*P,1ll*ans2*P);
    }
    return 0;
}