SDOI2009HH的项链

HH有一串由各种漂亮的贝壳组成的项链。HH相信不同的贝壳会带来好运,所以每次散步
完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH不断地收集新的贝壳,因此,
他的项链变得越来越长。有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同
的贝壳?这个问题很难回答。。。因为项链实在是太长了。于是,他只好求助睿智的你,来解

决这个问题。

在线貌似不可做?

可以用离线算法,先预处理出与当前位置颜色相同的下一个颜色在哪,然后对每一个询问按左端点排序,对于每一个询问,将其查询区间左边的所有“下一个颜色“位置的值加1,

答案即为(右端点)前缀和减去(左端点减一)前缀和之差。

答案正确性证明:

因为要相减,所以1-(左端点-1)不用考虑,因为相减抵消了,而在当前区间内,没出现过的不用讨论,而出现过的因为只计算其区间左端点之前的“下一个颜色位置”,所以不会计算多次,如下:

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=50000+10;
int a[maxn];
int next[1000010]={0};
int p[1000010]={0};
int s[maxn];
int n,m;
struct T{
    int l,r,id;
    bool operator<(const T&c)const{
        if(l==c.l)
            return r<c.r;
        return l<c.l;
    }
}A[200010];
int ans[200010];
inline void add(int x,int p){
    while(x<=n){
        s[x]+=p;
        x+=x&(-x);
    }
}
inline int sum(int x){
    int tmp=0;
    while(x){
        tmp+=s[x];
        x-=x&(-x);
    }
    return tmp;
}
int main(){
    freopen("diff.in","r",stdin);
    freopen("diff.out","w",stdout);
    int mx=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]),mx=max(mx,a[i]);
    for(int i=n;i>=1;i--)
        next[i]=p[a[i]],p[a[i]]=i;
    for(int i=0;i<=mx;i++)
        if(p[i])
            add(p[i],1);
    scanf("%d",&m);
    for(int i=1;i<=m;i++)
        scanf("%d %d",&A[i].l,&A[i].r),A[i].id=i;
    sort(A+1,A+m+1);
    int l=1;
    for(int i=1;i<=m;i++){
        while(l<A[i].l){
            if(next[l])
                add(next[l],1);
            l++;
        }
        ans[A[i].id]=sum(A[i].r)-sum(A[i].l-1);
    }
    for(int i=1;i<=m;i++)
        printf("%d\n",ans[i]);
return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值