bzoj 2742(树状数组)

传送门php

题意:

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

分析:

这个题目跟求区间内不一样的数的个数的解法很是类似。咱们在这题中一样采起离线树状数组的作法。数组

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

咱们将询问的区间按照左端点排序,并从左到右进行遍历,假若遍历到位置\(i\),在以后还存在颜色\(c_i\),即\(nex[i]\)存在,那么咱们须要删除\(nex[i]\)的贡献;同时若是\(nex[nex[i]]\)存在,即证实在区间\([~nex[i]~,~nex[nex[i]]~]\)中,\(c_i\)可以贡献答案,故咱们须要增长\(nex[nex[i]]\)的贡献。code

每次更新完以后咱们只须要在树状数组中查询询问区间的个数便可。排序

代码:

// 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;
}
相关文章
相关标签/搜索