289. 环路运输 - AcWing
题意
一个环形公路上有 n n n 个仓库,仓库之间的距离为两种走法的较小值,即 d i s ( i , j ) = m i n ( ∣ i − j ∣ , n − ∣ i − j ∣ ) dis(i,j)=min(|i-j|,n-|i-j|) dis(i,j)=min(∣i−j∣,n−∣i−j∣) 。从仓库 i i i 到仓库 j j j 的运输代价为 a i + a j + d i s ( i , j ) a_i+a_j+dis(i,j) ai+aj+dis(i,j) 。
求最大的运输代价。
解法
首先考虑把环展开成链,再复制一段。即
a
i
+
n
=
a
i
a_{i+n}=a_i
ai+n=ai。
a
i
+
a
j
+
d
i
s
(
i
,
j
)
=
a
i
+
a
j
+
m
i
n
(
i
−
j
,
n
−
i
+
j
)
,
满
足
i
∈
[
n
+
1
,
2
n
]
,
j
∈
[
i
−
n
+
1
,
i
−
1
]
=
{
a
i
+
a
j
+
n
−
i
+
j
,
j
∈
[
i
−
n
+
1
,
i
−
n
2
−
1
]
a
i
+
a
j
+
i
−
j
,
j
∈
[
i
−
n
2
,
i
−
1
]
a_i+a_j+dis(i,j)\\ =a_i+a_j+min(i-j,n-i+j),满足i\in[n+1,2n],j\in[i-n+1,i-1]\\ =\left\{\begin{matrix} a_i+a_j+n-i+j,\ \ j\in[i-n+1,i-\frac{n}{2}-1]\\ a_i+a_j+i-j,\ \ j\in[i-\frac{n}{2},i-1] \end{matrix}\right.
ai+aj+dis(i,j)=ai+aj+min(i−j,n−i+j),满足i∈[n+1,2n],j∈[i−n+1,i−1]={ai+aj+n−i+j, j∈[i−n+1,i−2n−1]ai+aj+i−j, j∈[i−2n,i−1]
所以我们可以从
n
+
1
n+1
n+1 到
2
n
2n
2n 遍历有端点
i
i
i ,然后用滑动窗口来维护
a
j
+
n
−
j
a_j+n-j
aj+n−j 和
a
+
j
−
j
a+j-j
a+j−j ,窗口的长度为
n
2
\frac{n}{2}
2n 。那么
a
n
s
=
m
a
x
i
=
n
+
1
2
n
(
a
i
−
i
+
q
1
l
1
,
a
i
+
i
+
q
2
l
2
)
ans=max_{i=n+1}^{2n}(a_i-i+q1_{l1},a_i+i+q2_{l2})
ans=maxi=n+12n(ai−i+q1l1,ai+i+q2l2) ,其中
q
1
l
1
,
q
2
l
2
q1_{l1},q2_{l2}
q1l1,q2l2 分别表示两个滑动窗口内的最大值。
代码
#pragma region
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <vector>
using namespace std;
typedef long long ll;
#define tr t[root]
#define lson t[root << 1]
#define rson t[root << 1 | 1]
#define rep(i, a, n) for (int i = a; i <= n; ++i)
#define per(i, a, n) for (int i = n; i >= a; --i)
#pragma endregion
const int maxn = 2e6 + 5;
int n, a[maxn];
pair<int, int> q1[maxn], q2[maxn]; //单调递减
int l1 = 1, r1 = 0, l2 = 1, r2 = 0;
int main() {
scanf("%d", &n);
rep(i, 1, n) scanf("%d", &a[i]), a[i + n] = a[i];
for (int i = 1; i < n / 2; ++i) {
int p1 = i, p2 = i + n / 2;
while (l1 <= r1 && q1[r1].second < a[p1] + p1 + n) --r1;
q1[++r1] = {p1, a[p1] + p1 + n};
while (l2 <= r2 && q2[r2].second < a[p2] - p2) --r2;
q2[++r2] = {p2, a[p2] - p2};
}
int ans = 0;
for (int i = n + 1; i <= 2 * n; ++i) {
while (l1 <= r1 && q1[l1].first < i - n + 1) ++l1;
while (l2 <= r2 && q2[l2].first < i - n / 2) ++l2;
ans = max({ans, a[i] - i + q1[l1].second, a[i] + i + q2[l2].second});
int p1 = i - 1 - n / 2, p2 = i - 1;
while (l1 <= r1 && q1[r1].second < a[p1] + p1 + n) --r1;
q1[++r1] = {p1, a[p1] + p1 + n};
while (l2 <= r2 && q2[r2].second < a[p2] - p2) --r2;
q2[++r2] = {p2, a[p2] - p2};
}
printf("%d\n", ans);
}