Loading

[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;
}
posted @ 2025-05-13 10:47  Sktn0089  阅读(39)  评论(0)    收藏  举报