BZOJ1878: [SDOI2009]HH的项链

【传送门:BZOJ1878


简要题意:

  给出一个长度为n的数列,给出m个询问,每个询问输入l,r,输出数列中l到r的不同的数的个数


题解:

  看这数据范围就不能用在线来做,那就只能离线了

  很显然就是树状数组

  首先将询问按照l从小到大的排序排一遍

  设一个数组next,next[i]表示下一个与第i个数相同的数的位置

  我们从左到右扫一遍数列,如果扫到x这个位置,将x这个位置--,就next[x]这个位置++,然后就通过求前缀和的方法来得出每个询问的答案就可以了


参考代码:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int k[51000];
struct question
{
    int l,r,d,id;
}q[210000];
bool cmp(question n1,question n2){return n1.l<n2.l;}
bool cmpi(question n1,question n2){return n1.id<n2.id;}
int a[51000];
int lowbit(int x){return x&-x;}
int getsum(int x)
{
    int ans=0;
    while(x!=0)
    {
        ans+=a[x];
        x-=lowbit(x);
    }
    return ans;
}
int n;
void change(int x,int c)
{
    while(x<=n)
    {
        a[x]+=c;
        x+=lowbit(x);
    }
}
int c[1100000];
int next[51000];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&k[i]);
    int m;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].id=i;
    }
    sort(q+1,q+m+1,cmp);
    memset(next,0,sizeof(next));
    memset(c,0,sizeof(c));
    for(int i=n;i>=1;i--)
    {
        next[i]=c[k[i]];
        c[k[i]]=i;
    }
    memset(a,0,sizeof(a));
    for(int i=1;i<=1000000;i++) if(c[i]!=0) change(c[i],1);
    int l=1;
    for(int i=1;i<=m;i++)
    {
        while(l<q[i].l)
        {
            change(l,-1);
            if(next[l]!=0) change(next[l],1);
            l++;
        }
        q[i].d=getsum(q[i].r)-getsum(q[i].l-1);
    }
    sort(q+1,q+m+1,cmpi);
    for(int i=1;i<=m;i++) printf("%d\n",q[i].d);
    return 0;
}

 

posted @ 2017-12-28 13:48  Star_Feel  阅读(171)  评论(0编辑  收藏  举报