bzoj 2742(树状数组)

传送门

题意:

给你一个长度为n的数列,其中有c种颜色,有q个询问,每个询问问你在区间[l,r]中,有多少种颜色的个数大于2。

分析:

这个题目跟求区间内不同的数的个数的解法非常相似。我们在这题中同样采取离线树状数组的做法。

我们发现,倘若处于位置\(l\)的第\(i\)种颜色\(c_i\)要贡献答案,那么在位置\(l\)之后某个位置\(r\)必须要存在一个同样种类的颜色,那么在区间\([l,r]\)中颜色\(c_i\)就能贡献答案。那么我们不妨记录一个数组\(nex[i]\),代表当前处于位置\(i\)的颜色\(c_i\)的下一个出现的位置。我们发现之后的结点能否对答案进行贡献,取决于当前位置,因此我们考虑从左到右更新树状数组。

我们将询问的区间按照左端点排序,并从左到右进行遍历,倘若遍历到位置\(i\),在之后还存在颜色\(c_i\),即\(nex[i]\)存在,那么我们需要删除\(nex[i]\)的贡献;同时如果\(nex[nex[i]]\)存在,即证明在区间\([~nex[i]~,~nex[nex[i]]~]\)中,\(c_i\)能够贡献答案,故我们需要增加\(nex[nex[i]]\)的贡献。

每次更新完之后我们只需要在树状数组中查询询问区间的个数即可。

代码:

// luogu-judger-enable-o2
#include <bits/stdc++.h>
#define maxn 2000005
using namespace std;
struct Node{
    int l,r,pos;
    bool operator <(const Node &b)const{
        if(l==b.l) return r<b.r;
        else return l<b.l;
    }
}q[maxn];
int bit[maxn*2],a[maxn],mp[maxn],nex[maxn],ans[maxn];
int lowbit(int x){
    return x&-x;
}
void update(int pos,int val){
    for(int i=pos;i<maxn;i+=lowbit(i)){
        bit[i]+=val;
    }
}
int query(int pos){
    int res=0;
    for(int i=pos;i>0;i-=lowbit(i)){
        res+=bit[i];
    }
    return res;
}
int main()
{
    int n,c,m;
    scanf("%d%d%d",&n,&c,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    for(int i=n;i>=1;i--) {
        nex[i] = mp[a[i]];
        mp[a[i]] = i;
    }
    for(int i=1;i<=c;i++){
        if(mp[i]&&nex[mp[i]]){
            update(nex[mp[i]],1);
        }
    }
    for(int i=1;i<=m;i++){
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].pos=i;
    }
    sort(q+1,q+1+m);
    int pre=1;
    for(int i=1;i<=m;i++){
        for(;pre<q[i].l;pre++){
            if(nex[pre]) update(nex[pre],-1);
            if(nex[pre]&&nex[nex[pre]]) update(nex[nex[pre]],1);
        }
        ans[q[i].pos]=query(q[i].r)-query(q[i].l-1);
    }

    for(int i=1;i<=m;i++){
        printf("%d\n",ans[i]);
    }
    return 0;
}
posted @ 2019-08-13 15:00  ChenJr  阅读(242)  评论(0编辑  收藏  举报