LOJ #3006. 「JOISC 2015 Day 4」防壁
LOJ #3006. 「JOISC 2015 Day 4」防壁
首先有一個很顯然的結論是:對於每條線段,貪心地向詢問點移動直到覆蓋的方案一定是最優的。於是我們就得到了一個 \(\mathcal O(NM)\) 的暴力做法。
我們先考慮Subtask2也就是 \(a_i=0\) 的情況怎麼做。對於線段 \([0,t]\) 我們定義詢問點 \(x_i\) 是重要的當且僅當該線段在為了覆蓋 \(x_i\) 時做出了貢獻,否則則稱 \(x_i\) 是非重要的。關於這個定義容易有以下結論:
- 對於一個詢問點 \(x_i\) 如果其對於線段 \([0,t]\) 是重要的,那麼 \(x_i\) 對於線段 \([0,t-1]\)
- 如果詢問點 \(x_i\) 對線段 \([0,t]\) 是重要的,並且滿足關係 \(x_{i-1}<x_i\),那麼該線段在覆蓋了 \(x_i\) 之後的位置是 \([x_i-t,x_i]\);反之若滿足關係 \(x_{i-1}>x_i\),那麼位置就是 \([x_i,x_i+t]\)。
對於詢問點 \(x_i\) 我們定義 \(f(x_i)\) 是滿足”\(x_i\) 對線段 \([0,t]\) 是重要的“的最大的 \(t\)。先考慮如何求出 \(f(x_i)\)。注意到 \(f(x_i)\) 的極值性,我們考慮整體二分。對於線段 \([0,t]\) 我們要判斷 \(x_i\)
然後我們思考在求出 \(f(x_i)\) 之後如何求出答案。假設當前的線段為 \([0,t]\),容易發現若詢問點 \(x_i\) 對該線段是非重要的,那麼它對這個線段沒有任何影響。於是只需要考慮所有對 \([0,t]\) 重要的詢問點 \(x_i\) 組成的詢問點序列 \(y_1\ldots y_m\)。容易發現 \(y_1\ldots y_m\) 對 \([0,t]\) 都有貢獻,不妨先假設線段 \([0,t]\)
然後我們考慮一般情況 \([a_i,b_i]\)。設 \(t_i=b_i-a_i\) 我們考慮算出 \([a_i,b_i]\) 的答案減去 \([0,t_i]\) 的答案 ( 記其為 \(w\) ) 於是觀察線段 \([a_i,b_i]\) 和 \([0,t_i]\) 的移動。找到最小的 \(p\) 滿足存在 \(q<p\) 使得 \(|x_p-x_q|>t_i\)。若不存在這樣的 \(p\) 此時線段 \([a_i,b_i]\) 和 \([0,t_i]\) 只會分別往一個方向移動,這是很容易的。對於新定義的 \(p\) 我們可以發現一些結論:
- 在覆蓋完詢問點 \(x_p\) 之後線段 \([a_i,b_i]\) 和 \([0,t_i]\) 位置一樣;
- 在覆蓋詢問點 \(x_p\) 之前線段 \([a_i,b_i]\) 和 \([0,t_i]\) 只會分別往一個方向移動。
於是我們直接二分求出 \(p\) 之後就可以很容易算出 \(w\) 了。這樣一般情況也做完了。
時間複雜度為 \(\mathcal O(N\log M+M\log N)\),空間複雜度為 \(\mathcal O(n)\)。由於所有帶 \(\log\) 的地方都是二分,因此該做法的常數較小。
參考程式碼
#include <bits/stdc++.h>
using namespace std;
static constexpr int inf = 0x3f3f3f3f;
static constexpr int Maxn = 2e5 + 5;
int n, m, a[Maxn], b[Maxn];
pair<int, int> c[Maxn], q[Maxn];
int64_t ans[Maxn];
int p[Maxn], dir[Maxn], pmn[Maxn], pmx[Maxn], pdif[Maxn];
int qi[Maxn], pt[Maxn], pre[Maxn];
void divide(int l, int r, int ql, int qr) {
if (l > r || ql > qr) return ;
if (l == r) {
for (int i = ql; i <= qr; ++i) pt[qi[i]] = l;
} else {
int mid = (l + r + 1) / 2, qn1 = 0, qn2 = 0;
static int q1[Maxn], q2[Maxn];
const int len = c[mid].first;
int xl = 0, xr = len, t = 0;
for (int i = ql; i <= qr; ++i) {
if (pre[qi[i]] > t) {
t = pre[qi[i]];
if ( dir[t]) xr = p[t], xl = xr - len;
if (!dir[t]) xl = p[t], xr = xl + len;
}
if (xr < p[qi[i]] || xl > p[qi[i]]) {
t = q2[++qn2] = qi[i];
if (xr < p[t]) xr = p[t], xl = xr - len;
if (xl > p[t]) xl = p[t], xr = xl + len;
} else q1[++qn1] = qi[i], pre[qi[i]] = t;
}
for (int i = 1; i <= qn1; ++i) qi[ql + i - 1] = q1[i];
for (int i = 1; i <= qn2; ++i) qi[ql + qn1 + i - 1] = q2[i];
divide(l, mid - 1, ql, qr - qn2);
divide(mid, r, qr - qn2 + 1, qr);
}
} // divide
int main(void) {
extern uint32_t readu32(void);
n = readu32(), m = readu32();
for (int i = 1; i <= n; ++i) {
a[i] = readu32(), b[i] = readu32();
c[i] = pair(b[i] - a[i], i);
} sort(c + 1, c + n + 1);
for (int i = 1; i <= m; ++i)
p[i] = readu32(), dir[i] = (p[i] > p[i - 1]);
iota(qi + 1, qi + m + 1, 1), divide(0, n, 1, m);
for (int i = 1; i <= m; ++i) q[i] = pair(pt[i], i);
sort(q + 1, q + m + 1);
int64_t cur = 0, turn = 0;
auto upd = [&](int x, int y, int w) {
if (x > m || y > m) return;
cur += w * abs(p[x] - p[y]);
turn += w * (dir[x] != dir[y]);
}; // main()::upd
auto calc = [&](int x) { return cur - turn * x; };
for (int i = 1; i <= m; ++i) upd(i - 1, i, 1);
static int prv[Maxn], nxt[Maxn];
for (int i = 1; i <= m; ++i) prv[i] = i - 1;
for (int i = 1; i <= m; ++i) nxt[i] = i + 1;
for (int i = 1, j = 1; i <= n; ++i) {
while (j <= m && q[j].first < i) {
int x = q[j].second;
int l = prv[x], r = nxt[x];
upd(l, x, -1), upd(r, x, -1);
nxt[l] = r, prv[r] = l;
upd(l, r, +1); ++j;
} ans[c[i].second] = calc(c[i].first);
}
pmn[0] = inf, pmx[0] = 0;
for (int i = 1; i <= m; ++i) {
pmn[i] = min(pmn[i - 1], p[i]);
pmx[i] = max(pmx[i - 1], p[i]);
pdif[i] = pmx[i] - pmn[i];
}
for (int i = 1; i <= n; ++i) {
const int len = b[i] - a[i];
int j = upper_bound(pdif + 1, pdif + m + 1, len) - pdif;
if (j == m + 1) {
auto inter = [&](int l1, int r1, int l2, int r2)
{ return max<int>(0, max(r2 - r1, l1 - l2)); };
int ori = inter(a[i], b[i], pmn[m], pmx[m]);
int cur = inter(0, len, pmn[m], pmx[m]);
ans[i] += ori - cur;
} else {
auto calc = [&](int l, int r) {
int64_t res = 0;
if (l > pmn[j - 1]) res += l - pmn[j - 1], l = pmn[j - 1], r = l + len;
else if (r < pmx[j - 1]) res += pmx[j - 1] - r, r = pmx[j - 1], l = r - len;
if (l > pmn[j]) res += l - pmn[j], l = pmn[j], r = l + len;
else if (r < pmx[j]) res += pmx[j] - r, r = pmx[j], l = r - len;
return res;
};
ans[i] += calc(a[i], b[i]) - calc(0, len);
}
}
for (int i = 1; i <= n; ++i)
printf("%lld\n", ans[i]);
exit(EXIT_SUCCESS);
} // main
// fast io
static const int _BUF_SIZE = 1 << 18;
static char _ibuf[_BUF_SIZE], *iS = _ibuf, *iT = _ibuf;
inline char getch(void) {
if (__builtin_expect(iS == iT, false))
iT = (iS = _ibuf) + fread(_ibuf, 1, _BUF_SIZE, stdin);
if (__builtin_expect(iS == iT, false)) return EOF;
else return *iS++;
} // getch
uint32_t readu32(void) {
register uint32_t x = 0;
register char ch = getch();
while (ch < '0' || ch > '9') ch = getch();
while (ch >= '0' && ch <= '9') ((x += (x << 2)) <<= 1) += (ch ^ '0'), ch = getch();
return x;
} // readu32