【BZOJ2095】【POI2010】Bridge(网络流,二分)

Description
YYD为了减肥,他来到了瘦海,这是一个巨大的海,海中有n个小岛,小岛之间有m座桥连接,两个小岛之间不会有两座桥,并且从一个小岛可以到另外任意一个小岛。现在YYD想骑单车从小岛1出发,骑过每一座桥,到达每一个小岛,然后回到小岛1。霸中同学为了让YYD减肥成功,召唤了大风,由于是海上,风变得十分大,经过每一座桥都有不可避免的风阻碍YYD,YYD十分ddt,于是用泡芙贿赂了你,希望你能帮他找出一条承受的最大风力最小的路线。
Input
输入:第一行为两个用空格隔开的整数n(2<=n<=1000),m(1<=m<=2000),接下来读入m行由空格隔开的4个整数a,b(1<=a,b<=n,a<>b),c,d(1<=c,d<=1000),表示第i+1行第i座桥连接小岛a和b,从a到b承受的风力为c,从b到a承受的风力为d。
Output
输出:如果无法完成减肥计划,则输出NIE,否则第一行输出承受风力的最大值(要使它最小)




最大值最小,想到二分。
那么接下来就是check的问题,check的主要作用就是判断这个由 ≤ \leq mid的边组成的图是否能构成欧拉回路。
因为本题要求的路线实际上是一个欧拉回路,那么我们可以在每个点的入度出度上做文章。
对于一个点u,因为u在欧拉回路中, i n [ u ] = = o u t [ u ] in[u]==out[u] in[u]==out[u],然而,在这个残余的图中,有些点的in,out是不相同的,但是这个图还是能构成欧拉回路,为什么?
因为在这个图中,有些的边是无向边,你在一种情况下in,out不相等是因为你取得是无向边的一种情况。
显然给一条无向边选方向是不会改变边两端的点的in-out=sum[u]的奇偶性(每次只会+2或-2),所以如果有一点sum值为奇数,显然不成立。
其次,如果有点不连通,也是显然不成立的。
那么我们可以想到一个建图:
s连接sum为正的点,流量为sum(表示多的流量)
sum为负的点连接t,流量为-sum(表示缺少的流量)
之间有无向边的两点连一条流量2的边(表示转化一次的流量)
满流意味着是欧拉回路。

int getfa(int x)//并查集判联通
{
    if(fa[x]==x)
    {
        return x;
    }
    return getfa(fa[x]);
}
bool check(int mid)//判断欧拉回路
{
    memset(in,0,sizeof(in));
    memset(out,0,sizeof(out));
    memset(head,0,sizeof(head));
    memset(vis,0,sizeof(vis));
    cnt=1;
    for(int i=1;i<=n;i++)
    {
        fa[i]=i;
    }
    for(int i=1;i<=m;i++)
    {
        if(zz[i]>mid&&hh[i]>mid)
        {
            return 0;
        }
        if(zz[i]<=mid)
        {
            in[yy[i]]++;
            out[xx[i]]++;
            vis[i]=1;
            fa[getfa(xx[i])]=getfa(yy[i]);
            if(hh[i]<=mid)
            {
                vis[i]=3;
            }
        }else{
            in[xx[i]]++;
            out[yy[i]]++;
            vis[i]=2;
            fa[getfa(yy[i])]=getfa(xx[i]);
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(abs(in[i]-out[i])&1)
        {
            return 0;
        }
        if((i!=n)&&(getfa(i)!=getfa(i+1)))
        {
            return 0;
        }
    }
    s=0;
    t=n+1;
    for(int i=1;i<=m;i++)
    {
        if(vis[i]==3)
        {
            adde(yy[i],xx[i],1);
            adde(xx[i],yy[i],0);
        }
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        if(in[i]==out[i])
        {
            continue;
        }
        if(in[i]>out[i])
        {
            adde(s,i,(in[i]-out[i])/2);
            adde(i,s,0);
        }else{
            adde(i,t,(out[i]-in[i])/2);
            adde(t,i,0);
            ans+=(out[i]-in[i])/2;
        }
    }
    dinic();
    return ans==maxnf;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d%d",&xx[i],&yy[i],&zz[i],&hh[i]);
        maxn=max(maxn,max(zz[i],hh[i]));
    }
    int l=1,r=maxn+1;//二分
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(check(mid))
        {
            r=mid;
        }else{
            l=mid+1;
        }
    }
    if(l>maxn)
    {
        puts("NIE");
    }else{
        printf("%d\n",l);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值