Ynoi2005 rmscne

发布时间 2023-07-21 08:29:57作者: Ender_32k

这东西在线不太能做,考虑离线扫描。扫描右端点 \(r\),我们对每个位置 \(l\) 维护一个 \(p_l\) 表示最小的 \(p\) 使得 \([l,p]\)\([l,r]\) 的合法子区间。

考虑如何维护 \(p_l\)。考虑新加入的右端点 \(r\),加入一个数 \(a_r\),上一次出现的位置为 \(lst_{a_r}=c\),那么对于 \(c\) 和以前的位置是没有影响的;而 \((c,r]\) 这段区间由于没有 \(a_r\) 这个数,那么直接覆盖为 \(r\) 即可。可以用线段树维护。

考虑查询,当前扫描到 \(r\),询问 \([l,r]\)。令 \(q_l\) 为最大的 \(q\) 使得 \([q,r]\) 合法,那么不难发现答案就是 \(\min\limits_{i\in[l,q_l]}\{p_i-i\}\)。这东西可以线段树上二分,但是多带了一半常数,估计过不去。

有一个很妙的并查集做法,学会了。

就是要对每个 \(l\) 维护 \(q_l\),当插入 \(r\) 前已经有一个 \(lst_{a_r}\) 了,那么直接将 \(q_{lst_{a_r}}\to lst_{a_r}+1\) 即可。因为 \(q_l\) 相当于 \([l,r]\) 所有出现过的数的 \(lst\)\(\min\)\(lst_{a_r}\) 这个位置可以替换成 \(r\) 了,所以直接求 \(q_{lst_{a_r}+1}\) 即可。


upd : 写完了,竟然是最优解前三。

const int maxn = 2e6 + 200;
const int inf = 1e9;
struct seg { int mn, co; seg () { mn = inf, co = -1; } } tr[maxn << 2];
int n, m, a[maxn], lst[maxn], ans[maxn], fa[maxn];
tu3 q[maxn];

int getf(int x) { return x == fa[x] ? x : fa[x] = getf(fa[x]); }
#define ls x << 1
#define rs x << 1 | 1
#define mid ((l + r) >> 1)
void pushup(int x) { tr[x].mn = min(tr[ls].mn, tr[rs].mn); }
void pushcov(int x, int l, int r, int c) { tr[x].co = c, tr[x].mn = c - r + 1; }
void pushdown(int x, int l, int r) {
    if (tr[x].co == -1) return; 
    pushcov(ls, l, mid, tr[x].co);
    pushcov(rs, mid + 1, r, tr[x].co);
    tr[x].co = -1;
}

void update(int l, int r, int s, int t, int c, int x) {
    if (s <= l && r <= t) return pushcov(x, l, r, c);
    pushdown(x, l, r);
    if (s <= mid) update(l, mid, s, t, c, ls);
    if (t > mid) update(mid + 1, r, s, t, c, rs);
    pushup(x);
}

int query(int l, int r, int s, int t, int x) {
    if (s <= l && r <= t) return tr[x].mn;
    pushdown(x, l, r);
    int res = inf;
    if (s <= mid) res = min(res, query(l, mid, s, t, ls));
    if (t > mid) res = min(res, query(mid + 1, r, s, t, rs));
    return res;
}

signed main() {
    n = read();
    for (int i = 1; i <= n; i++) a[i] = read(), fa[i] = i;
    m = read();
    for (int i = 1; i <= m; i++) {
        int l = read(), r = read();
        q[i] = mt(r, l, i);
    }
    sort(q + 1, q + m + 1);
    for (int i = 1, p = 1; i <= m; i++) {
        int l = get<1>(q[i]), r = get<0>(q[i]), id = get<2>(q[i]);
        while (p <= r) {
            update(1, n, lst[a[p]] + 1, p, p, 1);
            if (lst[a[p]]) fa[lst[a[p]]] = lst[a[p]] + 1;
            lst[a[p]] = p;
            p++;
        }
        r = getf(l);
        ans[id] = query(1, n, l, r, 1);
    }
    for (int i = 1; i <= m; i++) 
        write(ans[i]), puts("");
    return 0;
}