【树状数组】【P4113】[HEOI2012]采花

Description

给定一个长度为 \(n\) 的序列,有 \(m\) 次询问,每次询问一段区间,求区间中有多少个数出现次数超过 \(1\)

Limitation

\(n,~m~\leq~2~\times~10^6\)

Solution

好像和大众做法不大一样?

考虑对于每个位置,我们记一个 pre 和一个 post,代表他前面最后一个出现这个数的位置和他后面第一个出现这个数的位置。

考虑一个位置 \(x\) 对一个 \([l,~r]\) 的查询产生贡献当且仅当 \(pre_x~\geq~l\) 并且 \(post_x~>~r\)

我们发现这个东西是可以树状数组维护的。

考虑统计一段区间内 pre 大于等于某个数的个数显然可以对 pre 开权值树状数组,然后按照左端点不升序排序,左端点左移的时候将对应位置的 pre 加入树状数组,一次查询的答案即为 \(BIT_{r}~-~BIT_{l - 1}\)

现在考虑加入 \(post_x ~>~r\) 的限制。

我们发现当且仅当满足该条件的位置才对答案有贡献,于是我们只需要保证树状数组里只存有满足该条件的位置的 pre,就可以统计答案了。考虑按照右端点不升序排序,一开始先将最后一次出现的数字的 pre 加入到树状数组中,每次移动右端点的时候,将 post 为右端点所在位置的位置的 pre 加入树状数组即可(即将 \(post_x~=~R\)\(x\) 的对应 \(per_x\) 加入树状数组)。由于大于右端点的部分的 pre 对答案同样没有贡献了,所以移动右端点的时候别忘了把右端点对应的 pre 在树状数组上删除。

Code

#include <cstdio>
#include <vector>
#include <algorithm>
#ifdef ONLINE_JUDGE
#define freopen(a, b, c)
#endif

typedef long long int ll;

namespace IPT {
    const int L = 1000000;
    char buf[L], *front=buf, *end=buf;
    char GetChar() {
        if (front == end) {
            end = buf + fread(front = buf, 1, L, stdin);
            if (front == end) return -1;
        }
        return *(front++);
    }
}

template <typename T>
inline void qr(T &x) {
    char ch = IPT::GetChar(), lst = ' ';
    while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
    while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
    if (lst == '-') x = -x;
}

namespace OPT {
    char buf[120];
}

template <typename T>
inline void qw(T x, const char aft, const bool pt) {
    if (x < 0) {x = -x, putchar('-');}
    int top=0;
    do {OPT::buf[++top] = static_cast<char>(x % 10 + '0');} while (x /= 10);
    while (top) putchar(OPT::buf[top--]);
    if (pt) putchar(aft);
}

const int maxn = 2000010;

int n, c, m, dn;
int MU[maxn], oc[maxn], pos[maxn], pre[maxn], tree[maxn];
std::vector<int> vc[maxn];

struct Ask {
    int l, r, id, ans;
    
    inline bool operator<(const Ask &_others) const {
        return this->r > _others.r;
    }
};
Ask ask[maxn];
bool cmp(const Ask&, const Ask&);

int lowbit(int);
void update(int, int);
int query(int);

int main() {
    freopen("1.in", "r", stdin);
    qr(n); qr(c); qr(m); dn = n + 1;
    for (int i = 1; i <= n; ++i) {
        qr(MU[i]); pre[i] = oc[MU[i]]; oc[MU[i]] = i;
    }
    for (int i = 1; i <= c; ++i) oc[i] = dn;
    for (int i = n; i; --i) {
        vc[pos[i] = oc[MU[i]]].push_back(i);
        oc[MU[i]] = i;
    }
    for (int i = 1; i <= m; ++i) {
        qr(ask[i].l); qr(ask[i].r); ask[i].id = i;
    }
    std::sort(ask + 1, ask + 1 + m);
    for (auto i : vc[dn]) update(pre[i], 1);
    for (int i = 1, R = n; i <= m; ++i) {
        int l = ask[i].l, r = ask[i].r;
        while (R > r) {
            update(pre[R], -1);
            for (auto j : vc[R]) update(pre[j], 1);
            --R;
        }
        ask[i].ans = query(r) - query(l - 1);
    }
    std::sort(ask + 1, ask + 1 + m, cmp);
    for (int i = 1; i <= m; ++i) {
        qw(ask[i].ans, '\n', true);
    }
    return 0;
}

inline bool cmp(const Ask &_a, const Ask &_b) {
    return _a.id < _b.id;
}

inline int lowbit(int x) {return x & -x;}

void update(int x, int v) {
    if (x == 0) return;
    while (x <= dn) {
        tree[x] += v;
        x += lowbit(x);
    }
}

int query(int x) {
    int _ret = 0;
    while (x) {
        _ret += tree[x];
        x -= lowbit(x);
    }
    return _ret;
}

转载于:https://www.cnblogs.com/yifusuyi/p/10512857.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值