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

题目描述

题目传送门

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

分析:

其实你如果理解了题意 你会觉得这题很简单 (不是吗)
但它的数据:对于 100%的数据,1 ≤n,m,a_i ≤10^6,1 ≤l ≤r ≤n
昭示我们:别想着暴力离散化也不行
所以我们可采取的做法有:
线段树 主席树 树状数组 离线 莫队 以及二分前缀和
蒟蒻的我只能用线段树
过程也就是针对每个步骤分别写函数了
当然如果能用离线维护区间就更好了

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);  //最好用stable_sort(),因为没有cmp的结构体排序,sort()不稳定
    //当然如果你直接写cmp就没事了
    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;
}

速度还可以……

猜你喜欢

转载自blog.csdn.net/dgssl_xhy/article/details/107636294