Newcoder 139 J.Different Integers(BIT)

Description

给出一个长度为 n n 的序列a1,...,an q q 查询,每次查询(l,r)表示 a1,a2,...,ai,aj,aj+1,...,an a 1 , a 2 , . . . , a i , a j , a j + 1 , . . . , a n 中不同数字的个数

Input

多组用例,每组用例首先输入两个整数 n,q n , q ,之后输入 n n 个整数a1,...,an,最后 q q 行每行两个整数li,ri

(1n,q105,1ain,1li,rln) ( 1 ≤ n , q ≤ 10 5 , 1 ≤ a i ≤ n , 1 ≤ l i , r l ≤ n )

Output

对于每组查询输出查询结果

Sample Input

3 2
1 2 1
1 2
1 3
4 1
1 2 3 4
1 3

Sample Output

2
1
3

Solution

首先复制 a a 序列为a1,...,an,a1,...,an,同样记该序列为 a a ,那么对原序列的查询(l,r)即为求新序列 ar,...,al+n a r , . . . , a l + n 中出现的数字种类数,进而把对两个区间的查询变成一段连续区间。查询序列区间 [l,r] [ l , r ] 中数字种类数,即看每种数字在该区间是否出现过,进而等价于判断区间 [1,r] [ 1 , r ] 中该数字个数减去区间 [1,l1] [ 1 , l − 1 ] 中该数字个数是否为 0 0

对每种数字得到其第一次出现的位置,在树状数组中该位置加一,之后对于每种数字,从第一次出现的位置一个个往后跳到该种数字的位置,一直到跳入区间[l,r]为止,那么此时由树状数组可以求出 [1,r] [ 1 , r ] 中该种数字出现的次数以及 [1,l1] [ 1 , l − 1 ] 中该种数字出现的次数,两者相减若为 1 1 说明区间[l,r]中出现过该数字。对于多组查询,把查询离线按左端点排序,每次从不超过左端点的新位置往后跳,每次把跳到的位置在树状数组中加一表示该种数字数量加一,跳入区间 [l,r] [ l , r ] 则停即可

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define maxn 200005
int n,q,a[maxn],Next[maxn],ans[maxn],pos[maxn];
struct BIT
{
    #define lowbit(x) (x&(-x))
    int b[maxn];
    void init()
    {
        memset(b,0,sizeof(b));
    }
    void add(int x)
    {
        while(x<=n)
        {
            b[x]++;
            x+=lowbit(x);
        }
    }
    int sum(int x)
    {
        int ans=0;
        while(x)
        {
            ans+=b[x];
            x-=lowbit(x);
        }
        return ans;
    }
}bit;
struct query
{
    int l,r,id;
    bool operator<(const query&b)const
    {
        return l<b.l;
    }
}e[maxn];
int main()
{
    while(~scanf("%d%d",&n,&q))
    {
        for(int i=1;i<=n;i++)scanf("%d",&a[i]),a[i+n]=a[i];
        for(int i=1;i<=q;i++)
        {
            scanf("%d%d",&e[i].r,&e[i].l);
            e[i].r+=n;
            e[i].id=i;
        }
        sort(e+1,e+q+1);
        bit.init();
        memset(pos,0,sizeof(pos));
        n+=n;
        for(int i=n;i>=1;i--)
        {
            Next[i]=pos[a[i]];
            pos[a[i]]=i;
        }
        memset(pos,0,sizeof(pos));
        for(int i=1;i<=n;i++)
        {
            if(!pos[a[i]])bit.add(i);
            pos[a[i]]=i;
        }
        for(int i=1,j=1;i<=q;i++)
        {
            while(j<e[i].l)
            {
                if(Next[j])bit.add(Next[j]);
                j++;
            }
            ans[e[i].id]=bit.sum(e[i].r)-bit.sum(e[i].l-1);
        }
        for(int i=1;i<=q;i++)printf("%d\n",ans[i]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值