洛谷P2754 [CTSC1999]家园

Address


Solution

  • 一直在想费用流怎么处理周期,但其实可以枚举答案然后用最大流判断。
  • 考虑把太空船和地月球拆点,分别表示太空船和地月球每一个时刻的情况。
  • 每次枚举到一个时刻:
    • 从源点向这一时刻的地球连一条容量为 的边,表示地球上的人可以从任意时刻出发。
    • 从这一时刻的月球向汇点连一条容量为 的边,表示人可以在任意时刻转移到月球上。
    • 对于每个太空站,从上一时刻的点向这一时刻的点连一条容量为 的边,表示每个太空站可以容纳无穷多的人,并且呆在太空站的人可以从任意时刻出发。
    • 对于每个太空船,从上一时刻停靠的点向这一时刻停靠的点连一条容量为飞船容量 Hi H i 的边,表示 Hi H i 个人可以乘坐这艘太空船到达另一个太空站。
    • 新建完边后跑最大流,若流量 K ≥ K 则停止枚举。
  • 无解情况可以用并查集合并所有太空船停靠的点,再判断地月球是否连通即可。

Code

#include <iostream>
#include <cstdio>
#include <cctype>
#include <algorithm>
#include <cstdlib>

using namespace std;

inline int get()
{
    char ch; int res = 0; bool flag = false;
    while (ch = getchar(), !isdigit(ch) && ch != '-');
    (ch == '-' ? flag = true : res = ch ^ 48);
    while (ch = getchar(), isdigit(ch))
        res = res * 10 + ch - 48;
    return flag ? -res : res; 
}

const int Maxn = 0x3f3f3f3f;
const int N = 1e5 + 5, M = 2e5 + 5, L = 105;
int cur[N], lst[N], to[M], flw[M], nxt[M], lev[N], que[N];
int ans, n, qr, sum, src, des, cnt, m, K, T = 1, fa[L], h[L], a[L][L];

inline void Link(int x, int y, int z)
{
    nxt[++T] = lst[x]; lst[x] = T; to[T] = y; flw[T] = z;
    nxt[++T] = lst[y]; lst[y] = T; to[T] = x; flw[T] = 0;
}

inline bool Bfs()
{
    for (int i = 1; i <= cnt; ++i) 
        lev[i] = -1, cur[i] = lst[i];
    lev[src] = 0; que[qr = 1] = src; int x, y;
    for (int j = 1; j <= qr; ++j)
        for (int i = lst[x = que[j]]; i; i = nxt[i])
            if (flw[i] && lev[y = to[i]] == -1)
            {
                lev[y] = lev[x] + 1; que[++qr] = y;
                if (y == des) return true;
            }
    return false;
}

inline int Min(int x, int y) {return x < y ? x : y;}
inline int Dinic(int x, int flow)
{
    if (x == des) return flow;
    int del, res = 0, y;
    for (int &i = cur[x]; i; i = nxt[i])
        if (flw[i] && lev[y = to[i]] > lev[x])
        {
            del = Dinic(y, Min(flow - res, flw[i]));
            if (del)
            {
                flw[i] -= del; flw[i ^ 1] += del;
                res += del; if (res == flow) break; 
            }
        }
    if (res != flow) lev[x] = -1;
    return res;
}

inline int Maxflow()
{
    int res = 0;
    while (Bfs()) res += Dinic(src, Maxn);
    return res;
}

inline int Find(int x)
{
    if (fa[x] != x) fa[x] = Find(fa[x]);
    return fa[x];
}

int main()
{
    src = 1; des = 2; cnt = 2;
    n = get(); m = get(); K = get();
    for (int i = 1, im = n + 2; i <= im; ++i) fa[i] = i;
    for (int i = 1; i <= m; ++i)
    {
        h[i] = get(); a[i][0] = get();
        for (int j = 1; j <= a[i][0]; ++j)
        {
            a[i][j] = get();
            if (a[i][j] == 0) a[i][j] = n + 1;
            if (a[i][j] == -1) a[i][j] = n + 2;
        }
        for (int j = 2; j <= a[i][0]; ++j)
        {
            int tx = Find(a[i][j - 1]),
                ty = Find(a[i][j]);
            if (tx != ty) fa[tx] = ty;
        }
    }
    if (Find(n + 1) != Find(n + 2)) return puts("0"), 0;

    Link(src, cnt + n + 1, Maxn);
    Link(cnt + n + 2, des, Maxn); cnt += n + 2;
    for (ans = 1;; ++ans)
    {
        Link(src, cnt + n + 1, Maxn);
        Link(cnt + n + 2, des, Maxn);
        for (int i = 1, im; i <= n; ++i)
            Link(cnt - n - 2 + i, cnt + i, Maxn);
        for (int i = 1; i <= m; ++i)
            Link(a[i][(ans - 1) % a[i][0] + 1] + cnt - n - 2, 
                 a[i][ans % a[i][0] + 1] + cnt, h[i]);
        cnt += n + 2;
        sum += Maxflow();
        if (sum >= K) break;
    }   
    printf("%d\n", ans);
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值