家园 / 星际转移问题

题面

家园 / 星际转移问题

题解

一道比较新颖的题。
竟然是枚举答案加动态加边。
其实模拟一下不难发现,因为每个时刻飞船的位置是固定的,所以我们一开始就设法去建完整张图的话,会发现不能保证这个条件。
所以我们直接枚举时间,能够全部送到了,那肯定就是最短时间。然后每次在分层图上连接飞船上个时间处于的站台与这个时间的处于的站台。
直接跑最大流即可。
无解的情况用并查集维护是否连通即可。

代码

#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>

using namespace std;

const int N = 1e6 + 5;

int head[N], nex[N << 1], to[N << 1], w[N << 1], tot = 1, n, m, k, S, T;
int num[100], g[100][100], p[100], fa[100], ans, maxflow = 0;

inline void add(int u, int v, int f) {
	nex[++tot] = head[u]; to[tot] = v; w[tot] = f; head[u] = tot;
	nex[++tot] = head[v]; to[tot] = u; w[tot] = 0; head[v] = tot;
}

int Find(int x) { return x == fa[x] ? x : fa[x] = Find(fa[x]); }

void Union(int x, int y) {
	x = Find(x), y = Find(y);
	if(x == y) return ;
	fa[x] = y;
}

namespace Dinic {
	int dep[N], now[N];
	queue < int > q;
	
	inline bool bfs() {
		while(!q.empty()) q.pop();
		for(int i = 0; i <= ans * (n + 1); i++) dep[i] = 0; dep[T] = 0;
		q.push(S), dep[S] = 1; now[S] = head[S];
		while(!q.empty()) {
			int u = q.front(); q.pop();
			for(int i = head[u]; i; i = nex[i]) {
				if(!w[i] || dep[to[i]]) continue;
				q.push(to[i]);
				dep[to[i]] = dep[u] + 1;
				now[to[i]] = head[to[i]];
				if(to[i] == T) return true;
			}
		}
		return false;
	}
	int dinic(int x, int flow) {
		if(x == T) return flow;
		int rest = flow, k, i;
		for(i = now[x]; i && rest; i = nex[i])
			if(w[i] && dep[to[i]] == dep[x] + 1) {
				k = dinic(to[i], min(w[i], rest));
				if(!k) dep[to[i]] = 0;
				w[i] -= k;
				w[i ^ 1] += k;
				rest -= k;
			}
		now[x] = i;
		return flow - rest;
	}
}

int main() {
	scanf("%d%d%d", &n, &m, &k);
	for(int i = 1; i <= n + 2; i++) fa[i] = i;
	for(int i = 1; i <= m; i++) {
		scanf("%d%d", &p[i], &num[i]);
		for(int j = 0; j < num[i]; j++) {
			scanf("%d", &g[i][j]);
			if(g[i][j] == 0) g[i][j] = n + 1;
			if(g[i][j] == -1) g[i][j] = n + 2;
			if(j != 0) Union(g[i][j - 1], g[i][j]);
		}
	}
	if(Find(n + 1) != Find(n + 2)) {
		puts("0"); return 0;
	}
	for(ans = 1, S = 0, T = 1e6 + 1; ; ans++) {
		add(S, (ans - 1) * (n + 1) + n + 1, 0x3f3f3f3f);
		for(int i = 1, x, y; i <= m; i++) {
			x = (ans - 1) % num[i], y = ans % num[i];
			if(g[i][x] == n + 2) x = T;
			else x = (ans - 1) * (n + 1) + g[i][x];
			if(g[i][y] == n + 2) y = T;
			else y = ans * (n + 1) + g[i][y];
			add(x, y, p[i]);
		}
		while(Dinic::bfs()) maxflow += Dinic::dinic(S, 0x3f3f3f3f);
		if(maxflow >= k) {
			printf("%d\n", ans); return 0;
		}
		for(int i = 1; i <= n + 1; i++)
			add((ans - 1) * (n + 1) + i, ans * (n + 1) + i, 0x3f3f3f3f);
	}
	return 0;
}
posted @ 2021-07-29 22:37  init-神眷の樱花  阅读(45)  评论(0)    收藏  举报