题解 P2754 【[CTSC1999]家园 / 星际转移问题】

Infiltrator

2020-03-16 16:35:02

Solution

[$\Large\color{#FFBBFF}\textit{Tian-Xing's blog}$](https://Tian-Xing.github.io) ------------ # Description [传送门](https://www.luogu.com.cn/problem/P2754) ------------ # Solution 判断有无解可以使用并查集,如果最后地球和月球能处在同一个集合中,那么肯定可以到达,只是时间长短的问题。 因为多个飞船是同时飞行的,这样的问题不好直接处理。考虑星球的个数特别少,这时可以考虑按照时间轴建立分层图,$S$向每个时间点的地球连容量为$INF$的有向边,每个星球向下个时间点的同一个星球连容量为$INF$的有向边,表示这个星球的人等待到了下个时间点;每个飞船所在星球向飞船下个时间点到达的星球连容量为承载人的数量的有向边,表示可以进行飞行转移。 然后枚举时间,如果某个时间点最大流的比$k$大就输出时间,每次直接在残余网络上进行增广即可。 ------------ # Code ```cpp #include <iostream> #include <cstdio> #include <cstring> #include <queue> using namespace std; const int N = 50050; const int M = 100050; const int INF = 999999999; int n, m, s, t, head[N], num = 1, vis[N], d[N], mincost, maxflow, cur[N], k, a[5550][5550], fa[N], tim, b[500]; struct Node { int next, to, flow; } edge[M * 2]; void Addedge(int u, int v, int w) { edge[++num] = (Node){head[u], v, w}; head[u] = num; } void Add(int u, int v, int w) { // cout << u << " " << v << endl; Addedge(u, v, w); Addedge(v, u, 0); return; } template <class T> void Read(T &x) { x = 0; int p = 0; char st = getchar(); while (st < '0' || st > '9') p = (st == '-'), st = getchar(); while (st >= '0' && st <= '9') x = (x << 1) + (x << 3) + st - '0', st = getchar(); x = p ? -x : x; return; } template <class T> void Put(T x) { if (x < 0) putchar('-'), x = -x; if (x > 9) Put(x / 10); putchar(x % 10 + '0'); return; } int Find(int x) { return fa[x] == x ? fa[x] : Find(fa[x]); } void Merge(int a, int b) { int pa = Find(a), pb = Find(b); if (pa == pb) return; else fa[pa] = pb; } bool Bfs() { queue<int> q; for (int i = 0; i <= t; i++) d[i] = 0; d[s] = 1; q.push(s); while (!q.empty()) { int u = q.front(); q.pop(); for (int i = head[u]; i; i = edge[i].next) if (!d[edge[i].to] && edge[i].flow) { d[edge[i].to] = d[u] + 1; q.push(edge[i].to); if (edge[i].to == t) return 1; } } return 0; } int Dinic(int x, int flow) { if (x == t || !flow) return flow; int rest = flow; for (int i = head[x]; i && rest; i = edge[i].next) if (edge[i].flow && d[edge[i].to] == d[x] + 1) { int v = edge[i].to; int tmp = Dinic(v, min(rest, edge[i].flow)); rest -= tmp; edge[i].flow -= tmp; edge[i ^ 1].flow += tmp; if (!tmp) d[v] = 0; } return flow - rest; } int Maxflow() { int maxflow = 0, tmp; while (Bfs()) { tmp = Dinic(s, INF); if (tmp) maxflow += tmp; } return maxflow; } void Solve() { while(Bfs()) { for (int i = 0; i <= t; i++) cur[i] = head[i]; maxflow += Dinic(s, INF); } return; } int Id(int a, int b) { if (b == t) return t; return (n + 1) * a + b; } int main() { Read(n); Read(m); Read(k); t = 50000; for (int i = 1; i <= t; i++) fa[i] = i; for (int i = 1; i <= m; i++) { Read(b[i]); Read(a[i][0]); for (int j = 1; j <= a[i][0]; j++) { Read(a[i][j]); if (a[i][j] == 0) a[i][j] = n + 1; if (a[i][j] == -1) a[i][j] = t; if (j > 1) Merge(a[i][j], a[i][j - 1]); } Merge(a[i][1], a[i][a[i][0]]); } if (Find(t) != Find(n + 1)) { puts("0"); return 0; } for (tim = 1; ; tim++) { Add(s, Id(tim - 1, n + 1), INF); for (int i = 1; i <= m; i++) { int pos1 = tim % a[i][0] == 0 ? a[i][0] : tim % a[i][0], pos2 = (tim + 1) % a[i][0] == 0 ? a[i][0] : (tim + 1) % a[i][0]; // if (a[i][pos1] == t) continue; Add(Id(tim - 1, a[i][pos1]), Id(tim, a[i][pos2]), b[i]); } while(Bfs()) { maxflow += Dinic(s, INF); } if (maxflow >= k) { Put(tim); return 0; } for (int i = 1; i <= n + 1; i++) Add(Id(tim - 1, i), Id(tim, i), INF); } return 0; } ```