校内模拟赛 Attack's Fond Of LeTri

Attack's Fond Of LeTri

题意:

  n个房子m条路径边的无向图,每个房子可以最终容纳b个人,初始有a个人,中途超过可以超过b个人,每条边有一个长度,经过一条边的时间花费为边的长度。求所有人都进入房子的最小时间。如果不能容纳所有人,输出最少多少人无法进入房子。

分析:

  注意图不一定联通!!!

  首先Floyd一遍,求出任意两点之间的距离,二分一个答案,然后拆点建二分图,S想每个点连a的容量,另一个点向T连b的容量,对于两个点a,b,如果dis[a][b]<=二分的这个数,就加入一条左边到右边的边,容量为inf。满流即可。

代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cctype>
#include<cmath>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#define fore(i, u, v) for (int i = head[u], v = e[i].to; i; i = e[i].nxt, v = e[i].to) 
using namespace std;
typedef long long LL;

inline int read() {
    int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}
const int N = 5005, INF = 1e9;
struct Edge { int to, nxt, cap; } e[100005];
int head[N], q[50005], a[N], b[N], dis[N], cur[N];
int En = 1, S, T, n;
LL d[305][305], Sum;

inline void add_edge(int u,int v,int f) {
    ++En; e[En].to = v, e[En].nxt = head[u], e[En].cap = f; head[u] = En;
    ++En; e[En].to = u, e[En].nxt = head[v], e[En].cap = 0; head[v] = En;
}
bool bfs() {
    for (int i = 0; i <= T; ++i) dis[i] = -1, cur[i] = head[i];
    int L = 1, R = 0; q[++R] = S; dis[S] = 0;
    while (L <= R) {
        int u = q[L ++];
        fore(i, u, v) 
            if (dis[v] == -1 && e[i].cap > 0) {
                dis[v] = dis[u] + 1;
                if (v == T) return true;
                q[++R] = v;
            }
    }
    return false;
}
int dfs(int u,int flow) {
    if (u == T) return flow;
    int used = 0, tmp;
    for (int &i = cur[u]; i; i = e[i].nxt) {
        int v = e[i].to;
        if (dis[v] == dis[u] + 1 && e[i].cap > 0) {
            tmp = dfs(v, min(e[i].cap, flow - used));
            if (tmp > 0) {
                e[i].cap -= tmp, e[i ^ 1].cap += tmp; used += tmp;
                if (used == flow) break;
            }
        }
    }
    if (flow != used) dis[u] = -1;
    return used;
}
bool dinic(LL x, bool flag) {
    S = 0, T = n + n + 1, En = 1;
    memset(head, 0, sizeof(head));
    for (int i = 1; i <= n; ++i) add_edge(S, i, a[i]), add_edge(i + n, T, b[i]);
    for (int i = 1; i <= n; ++i) 
        for (int j = 1; j <= n; ++j) 
            if (d[i][j] <= x) add_edge(i, j + n, INF);
    LL ans = 0;
    while (bfs()) ans += dfs(S, INF);
    if (flag && ans != Sum) {
        cout << "NO\n" << Sum - ans; exit(0);
    }
    return ans == Sum;
}
void solve(int m) {
    for (int i = 1; i <= n; ++i) Sum += a[i];
//    memset(d, 0x3f, sizeof(d));
    for (int i = 1; i <= n; ++i) 
        for (int j = 1; j <= n; ++j) d[i][j] = 1e18;
    for (int i = 1; i <= n; ++i) d[i][i] = 0;
    for (int i = 1; i <= m; ++i) {
        int u = read(), v = read(), w = read(); 
        d[u][v] = min(d[u][v], (LL)w);
        d[v][u] = min(d[v][u], (LL)w);
    }
    for (int k = 1; k <= n; ++k) 
        for (int i = 1; i <= n; ++i) 
            for (int j = 1; j <= n; ++j) d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
    LL L = 1e18, R = 0, ans = 0;
    for (int i = 1; i <= n; ++i) 
        for (int j = 1; j <= n; ++j) if (d[i][j] != 1e18) L = min(L, d[i][j]), R = max(R, d[i][j]); // 注意要有dis[i][j]!=1e18???
    dinic(R, 1);
    while (L <= R) {
        LL mid = (L + R) >> 1;
        if (dinic(mid, 0)) R = mid - 1, ans = mid;
        else L = mid + 1;
    }
    cout << "YES\n" << ans << "\n";
}
int main() {
    n = read();int m = read(); 
    for (int i = 1; i <= n; ++i) a[i] = read(), b[i] = read();
    solve(m);
    return 0;
}

 

posted @ 2019-04-03 20:02  MJT12044  阅读(180)  评论(0编辑  收藏  举报