洛谷-1972 [SDOI2009]HH的项链

本文介绍了解决一个关于贝壳项链查询问题的三种算法:莫队算法、可持续化线段树和树状数组。通过这些算法,可以高效地计算出项链上任意一段包含的不同贝壳种类数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述
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 <= 500000,M <= 500000。

解释:1.莫队搞一下,按照块排序呢,最后在块里进行暴力,2可持续化线段树,维护从开头到结尾的求和线段树,值为维护last[i],i号节点相同的值最近出现的位置,然后我们对应区间找到相应的线段树再做查就好了。3.数状数组,我们按照右端点排序,然后维护最右端某点的贡献位置,如果前面出现相同的则去掉。最后区间求和就OK

//莫队
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath> 
using namespace std;
struct xxx{
    int l,r,block,id;
}q[200001];
int a[50001],cnt[1000100],ans[200001];
bool cmp(xxx a,xxx b){if(a.block!=b.block)return a.block<b.block;return a.r<b.r;}
int main()
{
    int n,m;scanf("%d",&n);int T=(int)sqrt((double)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",&q[i].l,&q[i].r);q[i].block=(q[i].l+1)/T;q[i].id=i;
    }
    sort(q+1,q+m+1,cmp);
    int l=1,r=0,sum=0;
    for(int i=1;i<=m;i++){
        while(r<q[i].r){cnt[a[++r]]++;if(cnt[a[r]]==1)sum++;}
        while(r>q[i].r){cnt[a[r]]--;if(cnt[a[r--]]==0)sum--;}
        while(l<q[i].l){cnt[a[l]]--;if(cnt[a[l++]]==0)sum--;}
        while(l>q[i].l){cnt[a[--l]]++;if(cnt[a[l]]==1)sum++;}
        ans[q[i].id]=sum;
    }
    for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
    return 0;
}
//类似树状数组的可持续化线段树
// It is made by XZZ
#include<cstdio>
#include<algorithm>
#define il inline
#define rg register
#define vd void
#define sta static
typedef long long ll;
il int gi(){
    rg int x=0,f=1;rg char ch=getchar();
    while(ch<'0'||ch>'9')f=ch=='-'?-1:f,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
const int maxn=50011;
int n;
namespace BITSegtree{
#define mid ((l+r)>>1)
    int rt[maxn],ls[maxn*100],rs[maxn*100],sum[maxn*100],index;
    il vd _update(int&x,int l,int r,const int&p,const int&k){
        if(!x)x=++index;sum[x]+=k;if(l==r)return;
        if(p<=mid)_update(ls[x],l,mid,p,k);
        else _update(rs[x],mid+1,r,p,k);
    }
    il int _query(int x,int l,int r,const int&L,const int&R){
        if( !x || r<L || R<l )return 0;if( L<=l && r<=R )return sum[x];
        return _query(ls[x],l,mid,L,R)+_query(rs[x],mid+1,r,L,R);
    }
    il int lb(const int&x){return x&-x;}
    il vd Update(int x,int y){while(x<=n)_update(rt[x],1,n+1,y+1,1),x+=lb(x);}
    il int Query(int l,int r,int x,int y){
        sta int ret;ret=0;
        while(r)ret+=_query(rt[r],1,n+1,x+1,y+1),r-=lb(r);
        --l;while(l)ret-=_query(rt[l],1,n+1,x+1,y+1),l-=lb(l);
        return ret;
    }
}
int lst[1001001];
int main(){
    //freopen("diff.in","r",stdin);
    //freopen("diff.out","w",stdout);
    using namespace BITSegtree;
    n=gi();int k;
    for(rg int i=1;i<=n;++i)k=gi(),Update(i,lst[k]),lst[k]=i;
    int l,r,m=gi();
    while(m--)l=gi(),r=gi(),printf("%d\n",Query(l,r,0,l-1));
    return 0;
}

//树状数组解法
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define lowbit(x) x&(-x)
using namespace std;
#define maxn 1000119
int num[maxn],tree[maxn],booll[maxn],nnn[maxn],N,ww;;
struct tt{
    int l,r;
    int pos;
};
tt ask[maxn];
bool cmp(tt x,tt y){
    return x.r<y.r;
}
void add(int n,int now){
    while(n<=N){
        tree[n]+=now;
        n+=lowbit(n);
    }
}
int sum(int n){
    int ans=0;
    while(n!=0){
        ans+=tree[n];
        n-=lowbit(n);
    }
    return ans;
}
int main(){
        scanf("%d",&N);
        for(int i=1;i<=N;i++)
            scanf("%d",&num[i]);
        scanf("%d",&ww);
        for(int i=1;i<=ww;i++){
            scanf("%d%d",&ask[i].l,&ask[i].r);
            ask[i].pos=i;
        }
        sort(ask+1,ask+1+ww,cmp);
        int next=1;
        for(int i=1;i<=ww;i++){
            for(int j=next;j<=ask[i].r;j++){
                if(booll[num[j]])
                    add(booll[num[j]],-1);
                add(j,1);
                booll[num[j]]=j;
            }
            next=ask[i].r+1;
            nnn[ask[i].pos]=sum(ask[i].r)-sum(ask[i].l-1);
        }
    for(int i=1;i<=ww;i++)
      cout<<nnn[i]<<endl;
    return  0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值