CF852D Exploration plan 题解

首先发现答案可以二分,考虑如何判定。

首先用最短路算法跑全源最短路预处理 di,jd_{i,j} 表示 iijj 最短路。用 Floyd 做复杂度是 O(n3)O(n^3) 的,若使用 Dijkstra 算法则是 O(nmlogm)O(nm \log m) 的,不过几种做法均可,因为复杂度瓶颈不在这里。

对于当前需要判定时间 xx 是否可以,我们发现任意一点对 (i,j)(i,j) 只需满足 di,jxd_{i,j} \leq x 即可进行移动。

考虑网络流模型:

  • 源点 SS 向每个有团队的点连边,容量为在这个点的团队数量。

  • 每一对 (i,j)(i,j) 满足 ii 点上有团队且 d(i,j)xd(i,j) \leq x,连 ij+ni \rightarrow j+n,容量为 ++\infty,表示可以移动的人没有限制。我们不需要判断 jj 是否有团队。加上 nn 是为了区分有团队的点和普通点。

  • 对于每个普通点 jj,连 j+nTj+n \rightarrow T,容量为 11,若满流表示有人在点上,否则没有。

STS \rightarrow T 的最大流 k\geq k,则时间 xx 可行,否则不可。

#include <bits/stdc++.h>
using namespace std;

const int M = 605, N = 1e6 + 5;

int n, m, p, k;
vector<pair<int, int> > G[N];

int dis[M][M], S = 0, T;
bool vis[N];

int e[N], h[N], c[N], ne[N], idx;
int g[N], pdd[N];
int d[N], cur[N];

inline void add(int u, int v, int w)
{
	e[idx] = v, ne[idx] = h[u], c[idx] = w, h[u] = idx++;
	e[idx] = u, ne[idx] = h[v], c[idx] = 0, h[v] = idx++;
}

struct Node
{
	int u, d;
	Node(int _u, int _d): u(_u), d(_d){}
	bool operator<(const Node& g) const
	{
		return d > g.d;
	}
};

inline void dijkstra(int s)
{
	for (int i = 1; i <= n; i++) vis[i] = 0;
	dis[s][s] = 0;
	priority_queue<Node> q;
	q.push(Node(s, 0));
	while (q.size())
	{
		int u = q.top().u;
		q.pop();
		if (vis[u]) continue;
		vis[u] = 1;
		for (auto j : G[u])
		{
			if (dis[s][j.first] > dis[s][u] + j.second)
			{
				dis[s][j.first] = dis[s][u] + j.second;
				q.push(Node(j.first, dis[s][j.first]));
			}
		}
	} 
}

inline bool bfs()
{
	for (int i = 0; i <= T; i++) d[i] = cur[i] = -1;
	queue<int> q;
	q.push(S);
	d[S] = 0, cur[S] = h[S];
	while (q.size())
	{
		int u = q.front();
		q.pop();
		for (int i = h[u]; ~i; i = ne[i])
		{
			int j = e[i];
			if (c[i] > 0 && d[j] == -1)
			{
				d[j] = d[u] + 1;
				cur[j] = h[j];
				if (j == T) return 1;
				q.push(j);
			}
		}
	}
	return 0;
}

inline int dfs(int u, int lim)
{
	if (u == T) return lim;
	int sum = 0;
	for (int i = cur[u]; ~i && sum < lim; i = ne[i])
	{
		cur[u] = i;
		int j = e[i];
		if (c[i] > 0 && d[j] == d[u] + 1)
		{
			int w = dfs(j, min(c[i], lim - sum));
			if (!w) d[j] = -1;
			sum += w;
			c[i] -= w;
			c[i ^ 1] += w;
		}
	}
	return sum;
}

inline int dinic()
{
	int res = 0;
	while (bfs())
	{
		int p;
		while (p = dfs(S, INT_MAX)) res += p;
	}
	return res;
}

inline bool check(int x)
{
	for (int i = 0; i <= T + 5; i++) h[i] = -1;
	idx = 0;
	for (int i = 1; i <= n; i++)
	{
		if (!pdd[i]) continue;
		for (int j = 1; j <= n; j++)
		{
			if (dis[i][j] <= x)
			{
				add(i, j + n, INT_MAX);
			}
		}
		add(S, i, pdd[i]);
	}
	for (int i = 1; i <= n; i++)
	{
		add(i + n, T, 1);
	}
	return (dinic() >= k);
}

int main()
{
	memset(h, -1, sizeof h);
	memset(dis, 0x3f, sizeof dis);
	scanf("%d%d%d%d", &n, &m, &p, &k);
	T = n + n + 1;
	for (int i = 1; i <= p; i++) scanf("%d", &g[i]), pdd[g[i]]++;
	for (int i = 1; i <= m; i++)
	{
		int u, v, w;
		scanf("%d%d%d", &u, &v, &w);
		G[u].emplace_back(make_pair(v, w));
		G[v].emplace_back(make_pair(u, w));
	}
	for (int i = 1; i <= n; i++)
	{
		dijkstra(i);
	}
	int l = 0, r = 1731312, ans = -1;
	while (l <= r)
	{
		int mid = l + r >> 1;
		if (check(mid)) ans = mid, r = mid - 1;
		else l = mid + 1;
	}
	printf("%d\n", ans);
	return 0;
}
posted @   HappyBobb  阅读(3)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示