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);
}