【洛谷 P1972】[SDOI2009]HH的项链【加强の线段树】

HH的项链是一个关于计数项链中不同贝壳种类的问题。给定项链长度和贝壳种类,以及多个询问区间,需要求解每个区间内不同贝壳种类的数量。此问题可以通过线段树的数据结构进行高效解答,支持区间查询并离线维护。
摘要由CSDN通过智能技术生成

题目描述

题目传送门

HH 有一串由各种漂亮的贝壳组成的项链。HH 相信不同的贝壳会带来好运,所以每次散步完后,他都会随意取出一段贝壳,思考它们所表达的含义。HH 不断地收集新的贝壳,因此,他的项链变得越来越长。

有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同的贝壳?这个问题很难回答…… 因为项链实在是太长了。于是,他只好求助睿智的你,来解决这个问题。

输入格式

一行一个正整数 n,表示项链长度。
第二行 n 个正整数 a_i,表示项链中第 i 个贝壳的种类。

第三行一个整数 m,表示 H 询问的个数。
接下来 m 行,每行两个整数 l,r,表示询问的区间。

输出格式

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

输入输出样例

输入 #1

6
1 2 3 4 3 5
3
1 2
3 5
2 6

输出 #1

2
2
4

分析:

线段树
过程也就是针对每个步骤分别写函数
当然如果能用离线维护区间就更好了

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#define p 1000001
#pragma GCC optimize(2)
using namespace std;
int a[p],qy[p],pos[p],ans[p];
struct node{
    int l,r,v;
}vis[p<<6];
struct node2{
    int l,r,id;
    bool operator<(const node2&a)const{简化cmp
        return r<a.r;
    }
}q[p];
void build(int l,int r,int dep)  //建树
{
    if(l==r){
        vis[dep].l=vis[dep].r=l;
        return ;
    }
    vis[dep].l=l;
    vis[dep].r=r;
    int mid=(l+r)>>1;
    build(l,mid,dep*2);
    build(mid+1,r,dep*2+1);
}
void next(int dep,int pos,int k)  //查找下一个点
{
    if(vis[dep].r==vis[dep].l){
        vis[dep].v=k;
        return ;
    }
    int mid=(vis[dep].r+vis[dep].l)>>1;
    if(pos<=mid){
        next(dep*2,pos,k);
    }else{
        next(dep*2+1,pos,k);
    }
    vis[dep].v=vis[dep*2].v+vis[dep*2+1].v;
}
int ques(int dep,int l,int r)  //询问 返回计数的值
{
    if(vis[dep].l==l&&vis[dep].r==r){
        return vis[dep].v;
    }
    int mid=(vis[dep].l+vis[dep].r)>>1;
    if(r<=mid){
        return ques(dep*2,l,r);
    }else if(l>mid){
        return ques(dep*2+1,l,r);
    }
    return ques(dep*2,l,mid)+ques(dep*2+1,mid+1,r);
}

int main()
{
    build(1,p,1); //先建树
    int n,m;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",a[i]);
        qy[i]=pos[a[i]];  //记录坐标
        pos[a[i]]=i;
    }
    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);
    int nxtr=1;
    for(int i=1;i<=m;i++){
        int l=q[i].l;
        int r=q[i].r;
        for(int i=nxtr;i<=r;i++){
            if(qy[i]!=0)  //查找了
                next(1,qy[i],0);  //就搜下一个
            next(1,i,1);
        }
        ans[q[i].id]=ques(1,l,r);  //赋值询问的答案
        nxtr=r+1;
    }
    for(int i=1;i<=m;i++){
        printf("%d\n",ans[i]);
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值