传送喵

题解

记数列长度为 $n$,询问数为 $q$

这题比较 $naive$的做法就是离散化颜色 (下文的颜色都指那个数列中的数,所谓不同数值也就是数不同的颜色个数) 之后,暴力跑个莫队,然后用树状数组统计答案,因为莫队是 $O(n\sqrt q)$的,转移的时候又要带个 $O(logn)$,复杂度妥妥爆了。

其实这个暴力已经很接近正解了,我们想想哪里可以优化改进

所谓莫队,它就是通过 $n\sqrt q$次单点移动区间两边的指针来离线回答询问的。

那么它本身用来转移的复杂度就是 $O(n\sqrt q)\times O(修改)$

我们再来想想询问,有些题目莫队询问是可以做到 $O(1)$的,而这题如果用树状数组维护当前的颜色信息,每次询问就是 $O(logn)$,总共是 $O(qlogn)$

所以一个呼之欲出的思路就是,我们能不能通过增加询问的复杂度来减少转移的复杂度。

差分维护颜色信息?的确修改会变成 $O(1)$,但是询问就总共 $O(nq)$了,咕咕咕。

其实我们可以直接分块搞呀。

因为是单点修改,使用分块我们的修改复杂度也是 $O(1)$的。

询问可以通过分块优化到 $O(q\sqrt n)$

然后总复杂度就是 $O(n\sqrt q+q\sqrt n)$啦,皆大欢喜。

#include<bits/stdc++.h>
#define fo(i, a, b) for(int i = (a); i <= (b); ++i)
#define N 300005
#define inf 1000000005
#define ls t[u].s[0]
#define rs t[u].s[1]
#define pb push_back
int n, m, block[N], sq1, b[N], a[N], belong[N], sq2;
struct node{
    int l, r, a, b, id;
    friend bool operator < (node x, node y)
    {
        return belong[x.l] != belong[y.l] ? belong[x.l] < belong[y.l] : belong[x.l] & 1 ? x.r > y.r : x.r < y.r;
    }
}t[N];
struct ques{
    int x, y;
}ans[N];
int tot, cnt[N], cnt2[N], c[N];
inline ques calc (int l, int r)
{
    ques ret = (ques) {0, 0};
    if (block[l] == block[r])
    {
        fo (i, l, r)
        {
            ret.x += c[i];
            ret.y += (bool) c[i];
        }
        return ret;
    }
    int bl = block[l] + 1, br = block[r] - 1;
    fo (i, bl, br)
    {
        ret.x += cnt[i];
        ret.y += cnt2[i];
    }
    bl = bl * sq1 - 1;
    fo (i, l, bl)
    {
        ret.x += c[i];
        ret.y += (bool) c[i];
    }
    br = (br + 1) * sq1;
    fo (i, br, r)
    {
        ret.x += c[i];
        ret.y += (bool) c[i];
    }
    return ret;
}
inline void add (int col, bool flag)
{
    if (flag)
    {
        if (!c[col]) ++cnt2[block[col]];
        ++c[col];
        ++cnt[block[col]];
    }
    else
    {
        --c[col];
        --cnt[block[col]];
        if (!c[col]) --cnt2[block[col]];
    }
}
int main ()
{
    scanf("%d %d", &n, &m);
    sq2 = std::max((int) (n / sqrt(1.0 * m * 2 / 3)), 1);
    fo (i, 1, n) belong[i] = i / sq2;
    fo (i, 1, n) {scanf("%d", &a[i]); b[i] = a[i];}
    tot = n;
    fo (i, 1, m)
    {
        scanf("%d %d %d %d", &t[i].l, &t[i].r, &t[i].a, &t[i].b);
        b[++tot] = t[i].a; b[++tot] = t[i].b;
        t[i].id = i;
    }
    std::sort(b + 1, b + tot + 1);
    tot = std::unique(b + 1, b + tot + 1) - b - 1;
    sq1 = (int) sqrt(tot);
    fo (i, 1, tot) block[i] = i / sq1;
    fo (i, 1, n) a[i] = std::lower_bound(b + 1, b + tot + 1, a[i]) - b;
    fo (i, 1, m) 
    {
        t[i].a = std::lower_bound(b + 1, b + tot + 1, t[i].a) - b;
        t[i].b = std::lower_bound(b + 1, b + tot + 1, t[i].b) - b;
    }
    std::sort(t + 1, t + m + 1);
    int L = 1, R = 0;
    fo (i, 1, m)
    {
        while (t[i].l < L) {--L; add(a[L], 1);}
        while (t[i].r > R) {++R; add(a[R], 1);}
        while (t[i].l > L) {add(a[L], 0); ++L;}
        while (t[i].r < R) {add(a[R], 0); --R;}
        ans[t[i].id] = calc(t[i].a, t[i].b);
    }
    fo (i, 1, m) printf("%d %d\n", ans[i].x, ans[i].y);
    return 0;
}
分类: 文章

quhengyi11

AFO 有缘再见

0 条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注