天天看點

「題解」洛谷 P4396 [AHOI2013]作業

題目

P4396 [AHOI2013]作業

思路

莫隊+樹狀數組,樹狀數組維護數出現的次數和是否出現過。

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define M 100001
#define lowbit(x) x & (-x)

int n, m, sqrn, a[M], num[M], ans1[M], ans2[M], cnt[M];
struct Query {
    int x, y, lm, rm, id;
    friend bool operator < (Query q1, Query q2) {
        if (num[q1.x] == num[q2.x]) return q1.y < q2.y;
        return num[q1.x] < num[q2.x];
    }
}q[M];
struct BIT {
    int c[M];
    void add(int x, int k) {
        for (; x <= n; x += lowbit(x)) c[x] += k;
    }
    int ask(int x) {
        int res = 0;
        for (; x > 0; x -= lowbit(x)) res += c[x];
        return res;
    }
}bt1, bt2;

void add(int x) {
    ++cnt[x], bt1.add(x, 1);
    if (cnt[x] == 1) bt2.add(x, 1);
}

void del(int x) {
    --cnt[x], bt1.add(x, -1);
    if (cnt[x] == 0) bt2.add(x, -1);
}

int main() {
    scanf("%d %d", &n, &m), sqrn = n / sqrt(m);
    if (!sqrn) ++sqrn;
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
        num[i] = (i - 1) / sqrn + 1;
    }
    for (int i = 1; i <= m; ++i) {
        scanf("%d %d %d %d", &q[i].x, &q[i].y, &q[i].lm, &q[i].rm);
        q[i].id = i;
    }
    std::sort(q + 1, q + m + 1);
    int l = 1, r = 1; cnt[a[1]] = 1;
    bt1.add(a[1], 1), bt2.add(a[1], 1);
    for (int i = 1; i <= m; ++i) {
        while (l > q[i].x) add(a[--l]);
        while (r < q[i].y) add(a[++r]);
        while (l < q[i].x) del(a[l++]);
        while (r > q[i].y) del(a[r--]);
        ans1[q[i].id] = bt1.ask(q[i].rm) - bt1.ask(q[i].lm - 1);
        ans2[q[i].id] = bt2.ask(q[i].rm) - bt2.ask(q[i].lm - 1);
    }
    for (int i = 1; i <= m; ++i) {
        printf("%d %d\n", ans1[i], ans2[i]);
    }
    return 0;
}