牛客网暑期ACM多校训练营(第一场)J 题

本文介绍了一种解决区间内不同整数计数问题的有效算法。通过扩展原数组并利用树状数组维护区间信息,实现了快速查询任意指定区间内的不同整数数量。此方法适用于编程竞赛及实际应用。

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

Given a sequence of integers a1, a2, ..., an and q pairs of integers (l1, r1), (l2, r2), ..., (lq, rq), find count(l1, r1), count(l2, r2), ..., count(lq, rq) where count(i, j) is the number of different integers among a 1, a2, ..., ai, aj, aj + 1, ..., an.

输入描述:

The input consists of several test cases and is terminated by end-of-file.

The first line of each test cases contains two integers n and q.

The second line contains n integers a1, a2, ..., an.

The i-th of the following q lines contains two integers l i and ri.

输出描述:

For each test case, print q integers which denote the result.

备注

* 1 ≤ n, q ≤ 10^5

* 1 ≤ ai ≤ n

* 1 ≤ li, ri ≤ n

* The number of test cases does not exceed 10.

示例1:

输入

3 2

1 2 1

1 2

1 3

4 1

1 2 3 4

1 3

输出

2

1

3

作者:scaufat
链接:https://www.nowcoder.com/discuss/87249?type=101&order=0&pos=6&page=0
来源:牛客网
 

首先将原数组扩展为2倍长的,即 a[i+n] = a[i]

对于查询a[1...l]和a[r..n]有多少种不同的数字可以转换为查询 a[r...l+n]有多少种不同的数字

首先考虑维护一个前缀和,pre[i]表示a[1...i]有多少种不同的数字,那么对于a[l...r]的答案就为pre[r] - pre[l-1] + 在a[l...r]和a[1...l-1]同时出现的数字的种类

对于如何求在a[l...r]和a[1...l-1]同时出现的数字的种类,可以考虑使用树状数组维护,树状数组的第i个点表示a[i]是否已经在1...l出现过,对于每个查询只需要查树状数组中l~r的区间和即可

那么我们只要对区间查询进行排序,对于左端每次右移的时候把对应的数的下一个位置加入到数状数组中即可。

AC代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <map>
 
using namespace std;
const int maxn=300000+5;
map<int,int> mp;
int data[maxn];
int a[maxn];
int ans[200000+5];
struct node{
    int l,r,id;
    bool operator<(node t)const{
        return r<t.r;
    }
}q[200000+5];
int sum(int i){
    int ans=0;
    while(i>0){
        ans+=data[i];
        i-=i&-i;
    }
    return ans;
}
void add(int i,int x){
    while(i<maxn){
        data[i]+=x;
        i+=i&-i;
    }
}
 
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        fill(data,data+n*2+2,0);
        mp.clear();
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            a[i+n]=a[i];
        }
        n=n*2;
 
 
        for(int i=0;i<m;i++){
            int x,y;
            scanf("%d%d",&x,&y);
            q[i].l=y;
            q[i].r=x+n/2;
            q[i].id=i;
        }
        sort(q,q+m);
        int pre=1;
        for(int i=0;i<m;i++){
            for(int j=pre;j<=q[i].r;j++){
                if(mp[a[j]]!=0){
                    add(mp[a[j]],-1);
                }
                add(j,1);
                mp[a[j]]=j;
            }
            pre=q[i].r+1;
            ans[q[i].id]=sum(q[i].r)-sum(q[i].l-1);
        }
        for(int i=0;i<m;i++){
            printf("%d\n",ans[i]);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值