P5331 [SNOI2019]通信

题目链接

由“每个哨站只能被后面的至多一个哨站连接”可以想到二分图网络流。我们有一个 (n^2) 暴力建图的方法:((S, x, 1, 0),(x,y',1,|a_x-a_y|),(y',T,1,0),(x,T,1,W))

然而这样将会有 (n^2) 条边。

不过我们发现,每个点都只会和其左边的所有点连边,因此可以想到线段树优化建图。不过每条边的权值都不一样,因此这个方法行不通。

然后考虑类似CDQ分治一样,考虑左边对左边,左边对右边,右边对右边的影响。我们可以在当前分治区间内把左边单拎出来,右边单拎出来,这样右边就可以放心大胆地连左边了。

然后观察边权的特殊性。由于边权是和点权差的绝对值有关,我们可以把点权都拎出来排序去重,看作一些点(一条链)。相邻的点之间流动需要耗费点权差这么多的代价。然后边数就可以变成 (O(L)) 级别的了。总边数:(O(nlogn))

关键代码:

void build(int L, int R) {
	if (L == R)	return ;
	int mid = (L + R) >> 1;
	htot = ltot = 0;
	for (register int i = L; i <= R; ++i)	a[++htot] = h[i];
	a[htot + 1] = -18364123;
	sort(a + 1, a + 1 + htot);
	for (register int i = 1; i <= htot; ++i)
		if (a[i] != a[i + 1])	lsh_a[++ltot] = a[i];
	for (register int i = 1; i < ltot; ++i)
		Addedge(ptot + i, ptot + i + 1, inf, lsh_a[i + 1] - lsh_a[i]),
		Addedge(ptot + i + 1, ptot + i, inf, lsh_a[i + 1] - lsh_a[i]);
	for (register int i = L; i <= mid; ++i) {
		int p = lower_bound(lsh_a + 1, lsh_a + 1 + ltot, h[i]) - lsh_a + ptot;
		Addedge(p, i + n, 1, 0);
	}
	for (register int i = mid + 1; i <= R; ++i) {
		int p = lower_bound(lsh_a + 1, lsh_a + 1 + ltot, h[i]) - lsh_a + ptot;
		Addedge(i, p, 1, 0);
	}
	ptot += ltot;
	build(L, mid), build(mid + 1, R);
}
原文地址:https://www.cnblogs.com/JiaZP/p/13470705.html