【bzoj1878】【洛谷P1972】【SDOI2009】HH的项链

21 篇文章 0 订阅

题目描述

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

输入输出格式

输入格式:

第一行:一个整数N,表示项链的长度。

第二行:N 个整数,表示依次表示项链中贝壳的编号(编号为0 到1000000 之间的整数)。

第三行:一个整数M,表示HH 询问的个数。

接下来M 行:每行两个整数,L 和R(1 ≤ L ≤ R ≤ N),表示询问的区间。

输出格式:

M 行,每行一个整数,依次表示询问对应的答案。

输入输出样例

输入样例#1:
6
1 2 3 4 3 5
3
1 2
3 5
2 6
输出样例#1:
2
2
4

说明

数据范围:

对于100%的数据,N <= 50000,M <= 200000。

题解

终于不是线段树裸题了,尽管还是有些水(对于大牛来说),但我了翘了3节自习才调出来(我还是太弱)
听说有神犇用莫队做,但我这种蒟蒻就只好用线段树了
大概思路是这样:
首先把数据预处理好,具体是看每一个是不是在前面有同类的,如果有就算一种。
然后在把每次查询以区间左端点从小到大排序(为什么能排序呢,因为没有修改操作),之后我们发现,如果下一个区间的左端点比上一个区间大的话,就需要更新了。当然不必担心更新的时间复杂度,我们只需要把在两个区间左端点之间的点全部舍去,再把同一种类的next点算上一种就可以了,复杂度O(logn)。然后后,你会惊讶的发现这就是—-区间求和!(蛤蛤)
代码如下(首先给大家看一下自己的wa记录)
WA记录

My Code

/**************************************************************
    Problem: 1878
    User: infinityedge
    Language: C++
    Result: Accepted
    Time:2572 ms
    Memory:13400 kb
****************************************************************/

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
struct node {
    int l1,r1,bh;
} a[200005];
int dt[200005];
int n,m,itmp[50005],ans[200005];
int nxt[1000005],pre[1000005],b[50005];
void init(int k,int l,int r) {
    if (r-l==1) {
        dt[k]=b[l];
        return;
    }
    int chl=k*2,chr=k*2+1;
    init(chl,l,(l+r)/2);
    init(chr,(l+r)/2,r);
    dt[k]=dt[chl]+dt[chr];
}
void updd(int a,int k,int l,int r) {
    if(a<l||a>=r)return;
    if (l==a&&r-l==1) {
        b[a]=1;
        dt[k]=b[a];
        return;
    }
    int chl=k*2,chr=k*2+1;
    updd(a,chl,l,(l+r)/2);
    updd(a,chr,(l+r)/2,r);
    dt[k]=dt[chl]+dt[chr];
}
int query(int a,int b,int k,int l,int r) {
    if(b<=l||a>=r)return 0;
    if(a<=l&&b>=r) {
        return dt[k];
    }
    if(r-l!=1) {
        int chl=k*2,chr=k*2+1;
        int res=query(a,b,chl,l,(l+r)/2)+query(a,b,chr,(l+r)/2,r);
        return res;
    }
}
int cmp(node a,node b) {
    if (a.l1==b.l1) return a.r1<b.r1;
    else return a.l1<b.l1;
}
int main() {
    memset(dt,0,sizeof(dt));
    memset(itmp,0,sizeof(itmp));
    memset(nxt,0,sizeof(nxt));
    memset(pre,0,sizeof(pre));
    scanf("%d",&n);
    for(int i=1; i<=n; i++) {
        scanf("%d",&itmp[i]);
    }
    for(int i=1; i<=n; i++) {
        nxt[pre[itmp[i]]]=i;
        if(!pre[itmp[i]])b[i]=1;
        pre[itmp[i]]=i;
    }
    init(1,1,n+1);
    scanf("%d",&m);
    for(int i=1; i<=m; i++) {
        scanf("%d%d",&a[i].l1,&a[i].r1);
        a[i].bh=i;
    }
    sort(a+1,a+m+1,cmp);
    for (int i=1; i<=m; i++) {
        if (a[i-1].l1!=a[i].l1)
            for (int j=a[i-1].l1; j<=a[i].l1-1; j++)
                if (nxt[j])updd(nxt[j],1,1,n+1);
                    ans[a[i].bh]=query(a[i].l1,a[i].r1+1,1,1,n+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、付费专栏及课程。

余额充值