HH的项链 树状数组练习

传送门:
AcWing

8561:HH的项链 (tzcoder.cn)

P1972 [SDOI2009] HH的项链 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

题目描述

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

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

输入格式

一行一个正整数 nn,表示项链长度。
第二行 nn 个正整数 aiai​,表示项链中第 ii 个贝壳的种类。

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

输出格式

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

输入输出样例

输入 #1复制

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

输出 #1复制

2
2
4

说明/提示

【数据范围】

对于 20%20% 的数据,1≤n,m≤50001≤n,m≤5000;
对于 40%40% 的数据,1≤n,m≤1051≤n,m≤105;
对于 60%60% 的数据,1≤n,m≤5×1051≤n,m≤5×105;
对于 100%100% 的数据,1≤n,m,ai≤1061≤n,m,ai​≤106,1≤l≤r≤n1≤l≤r≤n。

前言:一开始是打算用莫队写的 发现超时(我只会简单的莫队),后来发现可以用树状数组。(感觉这样写和莫队很像)

一般来说树状数组是用来快速维护前缀和的,但是这题是离线不带修的。

于是可以树状数组来写,但是要搞清楚为什么:

先存储询问(因为要排序),以 R 为关键排序 。

一段区间的不同数字我们需要保留的总是最后出现的,比如 1 2 3 1 ,询问你[1,4],那就要保留最后的1。

那有个问题,如何记录数字最新的位置呢?--------对ta使用map吧!

我们使用map来记录并更新一个数字最后出现的位置(也就是区间最“新”的位置)

那我们 也要重新遍历项链的数字,保留新的数字的计数,删去旧位置的计数。

就拿图举例(样例)

第一次询问[1,2]   0-2上只有1,2数字 对数量有1的贡献,0-0无数字  答案就是2;

第二次询问[3,5]   0-5上有1,2,3(后面又出现3,就要把他之前的贡献删去,否则会重复累加),4,3数字 对数量有1的贡献,0-2 有数字1,2  那么答案就是2;

第三次询问同理;

代码(无注释):

#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define ios ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define For(i,st,ed,der) for(int i=st;i<=ed;i+=der)

typedef long long ll;
typedef unsigned long long ull;
const int N = 1e6+10;


struct in{
    int l,r,id;
};
int m,n;
int a[N];
in b[N];
int res[N];
map<int,int>mp;
int tree[N];
int lowbit(int x)
{
    return x&(-x);
}

void add(int t,int x)
{
    while(t<=n)
    {
        tree[t]+=x;
        t+=lowbit(t);   
    }
}

int query(int t)
{
    int res=0;
    while(t)
    {
        
        res+=tree[t];
        t-=lowbit(t);
        
    }
    return res;
}


bool cmp(in a, in b)
{
    if(a.r!=b.r) return a.r<b.r;
    return a.l<b.l;
}
int main()
{
    ios;
   
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    
    cin>>m;
    for(int i=1;i<=m;i++)
    {
        cin>>b[i].l>>b[i].r;
        b[i].id=i;
    }
    sort(b+1,b+1+m,cmp);
    int t=1;
    for(int i=1;i<=m;i++)
    {
        int l=b[i].l;
        int r=b[i].r;
        int idx=b[i].id;
        while(t<=r)
        {
            if(mp[a[t]])
            {
                add(mp[a[t]],-1);
            }
            add(t,1);
            mp[a[t]]=t;
            t++;
        }
        res[idx]=query(r)-query(l-1);
    }
    
    for(int i=1;i<=m;i++) cout<<res[i]<<endl;
    
    return 0;
}

END


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值