牛客多校第一场 J.Different Integers

原博地址:https://www.nowcoder.com/discuss/87249

题目描述 

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 a1, 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 li and ri.

输出描述:

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

示例1

输入

复制

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

输出

复制

2
1
3

备注:

* 1 ≤ n, q ≤ 105
* 1 ≤ ai ≤ n
* 1 ≤ li, ri ≤ n
* The number of test cases does not exceed 10.

将数组复制一份放在原数组后面,求[l,r]内不同数的个数就变成了求[r,l+n]内有多少不同数字。

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

用辅助数组存好每个数字的下次出现的位置。

用树状数组维护,树状数组的第i个点表示a[i]是否已经在1...l出现过,出现过为1,没出现为0。树状数组求区间和就好。

#include<bits/stdc++.h>
#define maxn 200010
#define ll long long
#define INF 0x3f3f3f3f3f3f3f3f
using namespace std;
int a[maxn];
int bit[maxn];
int last[maxn];
int nxt[maxn];
int pre[maxn];
bool judge[maxn];
struct Query
{
    int l,r,id;
    friend bool operator < (const Query& A,const Query& B)
    {
        return A.l<B.l;
    }
}Q[maxn];
int lowbit(int x)
{
    return x&(-x);
}
void update(int x)
{
    for(int i=x;i<maxn;i+=lowbit(i))
        bit[i]++;
}
int query(int x)
{
    int ans=0;
    for(int i=x;i>0;i-=lowbit(i))
        ans+=bit[i];
    return ans;
}
int main()
{
    int n,q;
    while(scanf("%d%d",&n,&q)!=EOF)
    {
        memset(bit,0,sizeof(bit));
        memset(last,-1,sizeof(last));
        memset(judge,false,sizeof(judge));
        memset(pre,0,sizeof(pre));
        memset(nxt,-1,sizeof(nxt));
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            a[i+n]=a[i];
        }
        n=n*2;

        for(int i=1;i<=n;i++)
        {
            if(!judge[a[i]])
            {
                pre[i]=pre[i-1]+1;
                judge[a[i]]=true;
            }
            else
            {
                pre[i]=pre[i-1];
            }
            if(~last[a[i]])
                nxt[last[a[i]]]=i;
            last[a[i]]=i;
        }

        for(int i=0;i<q;i++)
        {
            scanf("%d%d",&Q[i].l,&Q[i].r);
            Q[i].l=Q[i].l+n/2;
            swap(Q[i].l,Q[i].r);
            Q[i].id=i;
        }
        sort(Q,Q+q);
        int ans[maxn];
        for(int i=0,k=1;i<q;i++)
        {
            while(k<Q[i].l)//l左边出现的所有数字的下次出现位置
            {
                if(~nxt[k])
                {
                    update(nxt[k]);
                }
                k++;
            }
            ans[Q[i].id]=pre[Q[i].r]-pre[Q[i].l-1]+query(Q[i].r)-query(Q[i].l-1);
        }
        for(int i=0;i<q;i++)
            printf("%d\n",ans[i]);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值