Universal OJ #637 Solution

闲话

这题非常简单。

题解

将所有的在 \([1,n+1]\) 范围内的数分成两组。一组已经出现过,另一组没有出现过。

考虑将询问离线,按左端点排序。

对于出现过的数 \(x\),其消失的可能性只有一种,那就是所询问区间包含 \(x\) 的所有出现位置且不包含 \(x-1\) 的位置。也就是说,当左端点落在 \(x\) 第一次出现位置前最近的 \(x-1\) 的位置右侧和 \(x\) 第一次出现位置或其左侧时,\(x\) 对答案产生 \(-1\) 的贡献当且仅当右端点落在 \(x\) 最后一次出现位置后最近的 \(x-1\) 位置左侧和 \(x\) 最后一次出现位置及其右侧之间。

对于未出现过的数 \(y\),其出现的可能性同样只有一种,那就是所询问区间包含至少一个 \(y-1\)。当左端点落在任意位置时,\(y\) 对答案产生 \(1\) 的贡献当且仅当右端点落在左端点后最近的 \(y-1\) 位置及其右侧。

扫描线即可。

时间复杂度 \(O((n+m)\log n)\)

代码

#include <algorithm>
#include <cstdio>
#include <tuple>
#include <utility>
#include <vector>
using namespace std;
const int N = 1e6 + 10;
int tr[N << 2], n, q, cidx, a[N], trs, res[N];
using t3i = tuple<int, int, int>;
using pii = pair<int, int>;
vector<pii> rgs[N];
vector<int> pos[N];
t3i qry[N];
void update(int x, int l, int r, int lb, int rb, int v)
{
    if (l >= lb and r <= rb)
    {
        tr[x] += v;
        return;
    }
    if (tr[x])
    {
        tr[x << 1] += tr[x], tr[x << 1 | 1] += tr[x];
        tr[x] = 0;
    }
    int mid = (l + r) >> 1;
    if (lb <= mid)
        update(x << 1, l, mid, lb, rb, v);
    if (rb > mid)
        update(x << 1 | 1, mid + 1, r, lb, rb, v);
}
int query(int x, int l, int r, int tar)
{
    if (l == r)
        return tr[x];
    if (tr[x])
    {
        tr[x << 1] += tr[x], tr[x << 1 | 1] += tr[x];
        tr[x] = 0;
    }
    int mid = (l + r) >> 1;
    if (tar <= mid)
        return query(x << 1, l, mid, tar);
    return query(x << 1 | 1, mid + 1, r, tar);
}
int main()
{
    scanf("%d%d", &n, &q);
    for (int i = 0; i <= n + 1; i++)
    {
        pos[i].emplace_back(0);
    }
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", a + i);
        pos[a[i]].emplace_back(i);
    }
    for (int i = 0; i <= n + 1; i++)
    {
        pos[i].emplace_back(n + 1);
        trs += (pos[i].size() != 2);
    }
    update(1, 1, n, 1, n, trs);
    // fprintf(stderr, "%d\n", trs);
    trs = 0;
    for (int i = 1; i <= n + 1; i++)
    {
        if (pos[i].size() > 2) // vanish
        {
            auto itp = lower_bound(pos[i - 1].begin(), pos[i - 1].end(), pos[i][1]);
            if (*itp <= pos[i][pos[i].size() - 2])
                continue;
            rgs[*prev(itp) + 1].emplace_back(-pos[i][pos[i].size() - 2], -*itp + 1);
            if (pos[i][1] + 1 > *itp - 1)
                continue;
            rgs[pos[i][1] + 1].emplace_back(pos[i][pos[i].size() - 2], *itp - 1);
            continue;
        }
        // generate
        if (pos[i - 1].size() == 2)
            continue;
        rgs[1].emplace_back(pos[i - 1][1], n);
        for (int j = 1; j < pos[i - 1].size() - 1; j++)
        {
            if (pos[i - 1][j] + 1 == pos[i - 1][j + 1])
                continue;
            rgs[pos[i - 1][j] + 1].emplace_back(-pos[i - 1][j] - 1, -pos[i - 1][j + 1] + 1);
        }
    }
    for (int i = 1; i <= q; i++)
    {
        auto &[l, r, id] = qry[i];
        scanf("%d%d", &l, &r);
        id = i;
    }
    sort(qry + 1, qry + q + 1);
    // for (int i = 1; i <= n; i++)
    // {
    //     fprintf(stderr, "t %d %llu\n", i, rgs[i].size());
    //     for (auto &[l, r] : rgs[i])
    //     {
    //         fprintf(stderr, "%d %d\n", l, r);
    //     }
    // }
    for (int i = 1; i <= q; i++)
    {
        auto &[l, r, id] = qry[i];
        // fprintf(stderr, "%d %d %d\n", l, r, id);
        while (trs < l)
        {
            trs++;
            for (auto &[tl, tr] : rgs[trs])
            {
                if (tl < 0)
                {
                    update(1, 1, n, -tl, -tr, -1);
                    continue;
                }
                update(1, 1, n, tl, tr, 1);
            }
        }
        res[id] = query(1, 1, n, r);
    }
    for (int i = 1; i <= q; i++)
    {
        printf("%d\n", res[i]);
    }
}
posted @ 2024-12-18 08:36  丝羽绫华  阅读(41)  评论(0)    收藏  举报