P1972 [SDOI2009]HH的项链

链接:Miku

--------------------------

%%%ljx巨佬会莫队

-------------------------

这道题可以用树状数组过

-------------------------

首先,把所有询问按照右端点从小到大排序,这个很容易想到,然后非常容易想到,建立一个数组f,

如果数字a在i出现了,就再f[i]=1,然后统计一下区间和就可以了,当然,用树状数组优化

------------------------

然而考虑一下这样的问题:区间不包含这个数了,这个数重复了

这个问题解决方法就是建立一个数组fl,记录这个数上一次出现的位置,然后对于每一个数,只保留最右边的那个就可以了

----------------------------

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m;
int a[1000005];
struct qu{
    int l;
    int r;
    int num;
}qj[1000005];
int fl[1000005];
bool cmp(qu x,qu y){
    return x.r<y.r;
}
int tree[1000005];
int ans[1000005];
int lowbit(int x){
    return x&-x;
}
void add(int x,int k){
    while(x<=n){
        tree[x]+=k;
        x+=lowbit(x);
    }
    return ;
}
int sum(int x){
    int ans=0;
    while(x){
        ans+=tree[x];
        x-=lowbit(x);
    }
    return ans;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;++i){
        scanf("%d",&a[i]);
    }
    scanf("%d",&m);
    for(int i=1;i<=m;++i){
        scanf("%d%d",&qj[i].l,&qj[i].r);
        qj[i].num=i;
    }
    sort(qj+1,qj+m+1,cmp);
    int r = 1;
    for(int i=1;i<=m;++i){
        while (r <= qj[i].r){//统计时间 
            add(r, 1);//先加上 
            if (fl[a[r]] != 0){//去重 
                add(fl[a[r]], -1);
            }
            fl[a[r]] = r;//重新记录上一次出现的位置 
            r++;
        }
        ans[qj[i].num] = sum(qj[i].r) - sum(qj[i].l - 1);
    }
    for (int i = 1; i <= m; i++) printf("%d\n", ans[i]);
    return 0;
}
Ac

 

posted @ 2020-02-15 18:34  Simex  阅读(106)  评论(0编辑  收藏  举报