bzoj 3236 [Ahoi2013]作业 莫队+树状数组

题面

题目传送门

解法

离线询问,然后莫队

建2棵权值树状数组,一棵为区间有多少个数,另一棵为区间有多少个不同的数

在扩展区间的时候用树状数组修改即可

代码

#include <bits/stdc++.h>
#define N 3000010
using namespace std;
template <typename node> void chkmax(node &x, node y) {x = max(x, y);}
template <typename node> void chkmin(node &x, node y) {x = min(x, y);}
template <typename node> void read(node &x) {
    x = 0; int f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int n, m, len, a[N], c[N], col[N], cnt[N], ans[N][2];
struct Node {
    int l, r, x, y, id;
} b[N];
struct BIT {
    int f[N];
    int lowbit(int x) {return x & -x;}
    void modify(int x, int v) {
        for (int i = x; i <= len; i += lowbit(i))
            f[i] += v;
    }
    int query(int x) {
        int ret = 0;
        for (int i = x; i; i -= lowbit(i))
            ret += f[i];
        return ret;
    }
} T1, T2;
bool cmp(Node a, Node b) {
    return col[a.l] == col[b.l] ? a.r < b.r : a.l < b.l;
}
void update(int x, int v) {
    cnt[a[x]] += v; T1.modify(a[x], v);
    if (v == 1)
        if (cnt[a[x]] == 1) T2.modify(a[x], 1);
    if (v == -1)
        if (!cnt[a[x]]) T2.modify(a[x], -1);
}
main() {
    read(n), read(m); len = 0;
    for (int i = 1; i <= n; i++)
        read(a[i]), c[++len] = a[i];
    int siz = (int)sqrt(n);
    for (int i = 1; i <= n; i++) col[i] = (i + siz - 1)  / siz;
    for (int i = 1; i <= m; i++) {
        read(b[i].l), read(b[i].r), read(b[i].x), read(b[i].y), b[i].id = i;
        c[++len] = b[i].x, c[++len] = b[i].y;
    }
    sort(c + 1, c + len + 1);
    len = unique(c + 1, c + len + 1) - c - 1;
    map <int, int> h;
    for (int i = 1; i <= len; i++) h[c[i]] = i;
    for (int i = 1; i <= n; i++) a[i] = h[a[i]];
    for (int i = 1; i <= m; i++)
        b[i].x = h[b[i].x], b[i].y = h[b[i].y];
    sort(b + 1, b + m + 1, cmp);
    for (int i = 1, l = 1, r = 0; i <= m; i++) {
        while (r < b[i].r) update(++r, 1);
        while (r > b[i].r) update(r--, -1);
        while (l < b[i].l) update(l++, -1);
        while (l > b[i].l) update(--l, 1);
        ans[b[i].id][0] = T1.query(b[i].y) - T1.query(b[i].x - 1);
        ans[b[i].id][1] = T2.query(b[i].y) - T2.query(b[i].x - 1);
    }
    for (int i = 1; i <= m; i++)
        cout << ans[i][0] << ' ' << ans[i][1] << "\n";
    return 0;
}

转载于:https://www.cnblogs.com/copperoxide/p/9478302.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值