BZOJ4669抢夺(费用流+二分答案)

题目描述

大战将至, 美国决定实行计划经济。美国西部总共有 N 个城市,编号
为 0 ∼ N − 1,以及 M 条道路,道路是单向的。其中城市 0 是一个大城
市,里面住着 K 个人,而城市 N − 1 是一个农业城市。现在所有城市 0 的
居民都需要到城市 N − 1 去领取食物。由于担心体力不支,所以居民都会
采取开车的形式出行。但道路不是无限宽的,对于一条道路,会有 ci 的限
制,表示在同一天内,最多只能有 ci 辆车同时在这条道路上行驶。一条道
路的长度为 1,每辆车的行驶速度也可以假定为 1 每天。城市 N − 1 会在
每个居民都到达后马上开始发放食物。现在 Reddington 想知道,假如在最
优安排下,居民最早能在多少天后领到食物。假如没有居民那就不需要发
放食物,默认为第 0 天。
题解
ORZ zyz
首先答案具有单调性,可以二分。
然后贪心一波,我们肯定要捡着最短路去走,然后我们增广出了一条路,因为时间时固定的,所以我们能通过的人数已经确定了。
就是(T-dis+1)*min(l)前面的是第一波人到终点到结束的时间, minl表示路径权值的最小值。
然后我们就一直重复这个过程,发现这个过程和EK费用流完全一样。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#define inf 1e18
#define N 1009
#define M 20002
using namespace std;
typedef long long ll;
queue<int>q; 
int dis[N],head[N],tot=1,pre[N],n,m,k;
ll fl[N],num;
bool vis[N];
inline int rd(){
    int x=0;char c=getchar();bool f=0;
    while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    return f?-x:x;
}
struct edge{int n,to,l,f;}e[M];
inline void add(int u,int v,int l,int f){
    e[++tot].n=head[u];e[tot].to=v;head[u]=tot;e[tot].l=l;e[tot].f=f;
    e[++tot].n=head[v];e[tot].to=u;head[v]=tot;e[tot].l=0;e[tot].f=-f;
}
inline bool spfa(int s,int t){
    memset(dis,0x3f,sizeof(dis));
    dis[s]=0;q.push(s);fl[s]=inf;
    while(!q.empty()){
        int u=q.front();q.pop();vis[u]=0;
        for(int i=head[u];i;i=e[i].n){
            int v=e[i].to;
            if(e[i].l&&dis[v]>dis[u]+e[i].f){
                dis[v]=dis[u]+e[i].f;pre[v]=i;fl[v]=min(fl[u],1ll*e[i].l);
                if(!vis[v]){vis[v]=1;q.push(v);}
            }
        }
    }
    return dis[t]!=0x3f3f3f3f;
}
struct node{
    int u,v,w;
}a[M];
inline void calc(int s,int t,ll mid){
    int x=t;
    while(x!=s){
        int i=pre[x];
        e[i].l-=fl[t];e[i^1].l+=fl[t];x=e[i^1].to;
    }
    num+=max(0ll,1ll*(mid-dis[t]+1)*fl[t]);
}
inline bool check(ll mid){
    memset(head,0,sizeof(head));tot=1;num=0;
    for(int i=1;i<=m;++i)add(a[i].u,a[i].v,a[i].w,1);    
    while(spfa(0,n-1))calc(0,n-1,mid);
    return num>=k;
}
int main(){
    while(scanf("%d%d%d",&n,&m,&k)!=EOF){
        for(int i=1;i<=m;++i){a[i].u=rd();a[i].v=rd();a[i].w=rd();}
        ll l=0,r=1e12,ans=-1;
        while(l<=r){
            ll mid=(l+r)>>1;
            if(check(mid)){
                ans=mid;r=mid-1;
            }else l=mid+1;
        }
        if(~ans)printf("%d\n",ans);else printf("No solution\n");
    }
    return 0;
}
posted @ 2019-01-05 20:07  comld  阅读(334)  评论(0编辑  收藏  举报