[IOI 2022] 无线电信号塔 做题记录
有点意思,是吗。link
这道题最关键的一步在于如何入手。
首先条件可以看成相邻两个塔的条件,接下来就不好做了。仔细观察,发现这个条件是可以拆开来的:设 \(x_i, y_i\) 为塔 \(i\) 左边和右边第一个高度至少为 \(a_i + D\) 的塔编号,那么相邻两个选择的塔 \(i, j(i < j)\) 需满足 \(y_i \le x_j\)。
那么条件可以直接转化为:选择若干个区间满足两两不交。
先思考只有 \(L = 1, R = n\) 的询问,此时 \(D\) 不固定。
考虑每个塔 \(i\) 的贡献,进一步发现这些区间要么不交,要么包含。塔 \(i\) 的贡献可能会被高度比它小的塔挤掉,先求出 \(l_i, r_i\) 表示左边和右边第一个高度比 \(a_i\) 小的塔的编号。
设 \(ld_i = \max\limits_{j = l_i} ^ i a_j - a_i, \ rd_i = \max\limits_{j = i} ^ {r_i} a_j - a_i, \ d_i = \min(ld_i, rd_i)\)。
那么当 \(D\le d_i\) 时塔 \(i\) 才有贡献。
显然可以拓展到 \(L, R\) 不固定的情况,预先对 \(d_i\) 扫描线建立主席树,可以查询 \(d_L\sim d_R\) 中 \(\le D\) 的数的个数。
但是还需要考虑 \(l_i < L\) 和 \(r_i > R\) 的情况,这对答案有影响,可以分类讨论:
-
\(l_i < L, r_i > R\):此时 \(a_i\) 一定是区间最小值,容易处理。
-
\(l_i < L, r_i \le R\):看似需要三维偏序,实则不然。这样的 \(i\) 一定是在区间前缀最小值序列上,设为 \(i_1, i_2, \dots, i_k\),那么我们相当于统计 \(ld_{i_j} < D \le rd_{i_j}\) 的个数。进一步观察,发现 \(\forall j, ld_{i_j} > \max(ld_{i_{j - 1}}, rd_{i_{j - 1}})\),所以这样的 \(j\) 至多存在一个,这样就不需要再写主席树了,直接倍增即可。
-
\(l_i \ge L, r_i > R\):同上。
时间复杂度 \(\mathcal O((n + q)\log n)\)。
这道题的入手点在于条件的转化。
原来的条件看起来真的很不可做,甚至容易引导选手思考更神秘的方法。
而把相邻两个塔的拆开来,条件就变得清晰明朗了,就变成了区间选择问题,进而变为偏序问题。
还有就是最后的分讨上,我原来想到直接写主席树来统计 \(ld_{i_j} < D \le rd_{i_j}\),但是可以通过更细致的观察,直接倍增就好。
这也说明我们不能一味地依赖黑盒,观察算法的本质,优化算法。
点击查看代码
#include <bits/stdc++.h>
#define ll int
#define LL long long
#define ull unsigned ll
#define fi first
#define se second
#define mkp make_pair
#define pir pair <ll, ll>
#define pb push_back
#define i128 __int128
using namespace std;
char buf[1 << 22], *p1, *p2;
// #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, (1 << 22) - 10, stdin), p1 == p2)? EOF : *p1++)
template <class T>
const inline void rd(T &x) {
char ch; bool neg = 0;
while(!isdigit(ch = getchar()))
if(ch == '-') neg = 1;
x = ch - '0';
while(isdigit(ch = getchar()))
x = (x << 1) + (x << 3) + ch - '0';
if(neg) x = -x;
}
const ll maxn = 1e5 + 10, inf = 1e9, mod = 1000002022;
ll power(ll a, ll b = mod - 2) {
ll s = 1;
while(b) {
if(b & 1) s = 1ll * s * a %mod;
a = 1ll * a * a %mod, b >>= 1;
} return s;
}
template <class T, class _T>
const inline ll pls(const T x, const _T y) { return x + y >= mod? x + y - mod : x + y; }
template <class T, class _T>
const inline ll mus(const T x, const _T y) { return x < y? x + mod - y : x - y; }
template <class T, class _T>
const inline void add(T &x, const _T y) { x = x + y >= mod? x + y - mod : x + y; }
template <class T, class _T>
const inline void sub(T &x, const _T y) { x = x < y? x + mod - y : x - y; }
template <class T, class _T>
const inline void chkmax(T &x, const _T y) { x = x < y? y : x; }
template <class T, class _T>
const inline void chkmin(T &x, const _T y) { x = x < y? x : y; }
ll n, a[maxn], l[maxn], r[maxn], p[maxn][20], q[maxn][20];
ll stk[maxn], top, Ld[maxn], Rd[maxn], d[maxn], h[maxn], ht, rt[maxn];
ll st[20][maxn], Log[maxn]; vector <ll> vec[maxn];
struct SGT {
ll sum[maxn * 20], lc[maxn * 20], rc[maxn * 20], tot;
void modify(ll &p, ll q, ll l, ll r, ll x) {
p = ++tot, sum[p] = sum[q] + 1;
if(l == r) return; ll mid = l + r >> 1;
lc[p] = lc[q], rc[p] = rc[q];
if(x <= mid) modify(lc[p], lc[q], l, mid, x);
else modify(rc[p], rc[q], mid + 1, r, x);
}
ll query(ll p, ll l, ll r, ll ql, ll qr) {
if(ql <= l && r <= qr) return sum[p];
if(!p || r < ql || qr < l) return 0;
ll mid = l + r >> 1;
return query(lc[p], l, mid, ql, qr)
+ query(rc[p], mid + 1, r, ql, qr);
}
} tr;
ll ask(ll l, ll r) {
ll k = Log[r - l + 1];
return max(st[k][l], st[k][r - (1 << k) + 1]);
}
void init(ll N, vector <ll> H) {
n = N;
for(ll i = 0; i < n; i++) a[i + 1] = H[i];
for(ll i = 1; i <= n; i++) {
r[i] = n + 1, st[0][i] = a[i];
while(top && a[i] < a[stk[top]]) r[stk[top--]] = i;
l[i] = stk[top], stk[++top] = i;
}
for(ll i = 1; i <= n; i++) {
p[i][0] = l[i], q[i][0] = r[i];
for(ll j = 1; j < 20; j++) p[i][j] = p[p[i][j - 1]][j - 1];
}
for(ll i = 0; i < 20; i++) q[n + 1][i] = n + 1;
for(ll i = n; i; i--)
for(ll j = 1; j < 20; j++) q[i][j] = q[q[i][j - 1]][j - 1];
for(ll i = 2; i <= n; i++) Log[i] = Log[i >> 1] + 1;
for(ll i = 1; (1 << i) <= n; i++)
for(ll j = 1; j + (1 << i) - 1 <= n; j++)
st[i][j] = max(st[i - 1][j], st[i - 1][j + (1 << i - 1)]);
for(ll i = 1; i <= n; i++) {
Ld[i] = ask(l[i] + 1, i) - a[i], Rd[i] = ask(i, r[i] - 1) - a[i];
d[i] = min(Ld[i], Rd[i]), h[++ht] = d[i];
} sort(h + 1, h + 1 + ht);
ht = unique(h + 1, h + 1 + ht) - h - 1;
for(ll i = 1; i <= n; i++)
vec[lower_bound(h + 1, h + 1 + ht, d[i]) - h].pb(i);
for(ll i = ht; i; i--) {
rt[i] = rt[i + 1];
for(ll j: vec[i])
tr.modify(rt[i], rt[i], 1, n, j);
}
}
ll max_towers(ll L, ll R, ll D) {
++L, ++R;
ll c = lower_bound(h + 1, h + 1 + ht, D) - h;
ll ans = tr.query(rt[c], 1, n, L, R);
ll x = L;
for(ll i = 19; ~i; i--)
if(q[x][i] <= R) x = q[x][i];
if(d[x] < D) ++ans;
x = L;
for(ll i = 19; ~i; i--)
if(q[x][i] <= R && Ld[q[x][i]] < D) x = q[x][i];
if(Ld[x] < D && Rd[x] >= D && r[x] <= R) ++ans;
x = R;
for(ll i = 19; ~i; i--)
if(p[x][i] >= L && Rd[p[x][i]] < D) x = p[x][i];
if(Rd[x] < D && Ld[x] >= D && l[x] >= L) ++ans;
return ans;
}

浙公网安备 33010602011771号