牛客网 Different Integers 题解

 

题目描述

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.

示例输入

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.

 

题意理解

题目给出N个数,Q次查询,每次查询区间[1,l]和[r,n]之间不相同的的数字的数量。

思考

Q次查询,范围有10^5,时间复杂度O(N)在外部循环预定。若用hash数组记录是否重复,每次查询从该区间的头枚举到尾求不同数的数量,不论是让更新复杂度为1查询复杂度为N,还是让更新复杂度为N查询复杂度为1,时间复杂度都为O(N),总时间复杂度O(N^2)无法接受。

 

本题有关键词区间查询区间更新,自然想到利用树状数组维护这两个操作,树状数组算法时间复杂度为O(logN),总时间复杂度O(NlogN),不会超时。

 

简化问题:大小为n的数组扩大一倍,用原数组再填充一遍新扩大的空数组,这样查询区间[1,l]和[r,n]这两个区间简化为查询[r,n+l]这一个区间。

用代码实现

创建结构体

struct Q
{
    int l,r;//区间首尾
    int pos;//记录这是第几次查询方便输出
}q[200005];

自定义结构体排序规则

//自定义结构体排序规则
int cmp(Q a, Q b)
{
    return a.l < b.l;
}

 

树状数组基本函数

//lowbit运算
int lowbit(int k)
{
    return k&(-k);
}

 

//更新操作
void modify(int n,int v)
{
    while(n <= N)
    {
        c[n] += v;
        n += lowbit(n);
    }
}

 

//查询操作
int sum(int n)
{
    int ans = 0;
    while(n > 0)
    {
        ans += c[n];
        n -= lowbit(n);
    }
return ans;
}

 

从尾遍历并处理

for(int i=N;i>=1;i--)
{
    if(!show[A[i]])//show没出现过
    {
        show[A[i]] = i;
        fir[i] = true;//当前位置的数是第一次出现
    }
else
    {   
        //show出现过
        Next[i] = show[A[i]];//记录他下一次出现的位置
        fir[Next[i]] = false;//下一个位置不是第一次出现
        fir[i] = true;//这个位置是第一次出现
        show[A[i]] = i;//记录出现位置
    }
}

经过上述遍历,整个数组的每一个数字有了 是否第一次出现、该数下一次出现位置的信息,让树状数组利用这些信息更新并查询。

查询结果处理

//求数组下标在[r,l]的和可以转换成sum(l)-sum(r-1)
res[q[i].pos] = sum(q[i].r) - sum(q[i].l-1);

完整代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
 
 
using namespace std;
 
 
int N,M;
const int SIZE = 250005;
int c[SIZE];
int A[SIZE];
int Next[SIZE];
int res[200005];
int show[2000005];
bool fir[SIZE];
 
 
struct Q
{
    int l,r;
    int pos;
}q[200005];
 
 
int lowbit(int k)
{
    return k&(-k);
}
 
 
void modify(int n,int v)
{
    while(n <= N)
    {
        c[n] += v;
        n += lowbit(n);
    }
}
 
 
int sum(int n)
{
    int ans = 0;
    while(n > 0)
    {
        ans += c[n];
        n -= lowbit(n);
    }
    return ans;
}
 
 
int cmp(Q a, Q b)
{
    return a.l < b.l;
}
 
 
int main()
{
    while(~scanf("%d",&N)){
    	memset(res,0,sizeof(res));
    	memset(c,0,sizeof(c));
    	memset(A,0,sizeof(A));
    	memset(Next,0,sizeof(Next));
    	memset(fir,0,sizeof(fir));
    	memset(show,0,sizeof(show));
    	scanf("%d",&M);
	    for(int i=1;i<=N;i++) scanf("%d",&A[i]);//A原数组 
	 	for(int i=1;i<=N;i++) A[N+i]=A[i];//A原数组 
	 	N=N*2;
	    for(int i=N;i>=1;i--)
	    {
	        if(!show[A[i]])//show没出现过 
	        {
	            show[A[i]] = i;
	            fir[i] = true;//当前位置的数是第一次出现 
	        }
	        else
	        {//show出现过 
	            Next[i] = show[A[i]];//记录他下一次出现的位置 
	            fir[Next[i]] = false;//下一个位置不是第一次出现 
	            fir[i] = true;//这个位置是第一次出现
	            show[A[i]] = i;//记录出现位置
	        }
	    }
	 
	    for(int i=1;i<=M;i++)
	    {
	    	int l,r;
	        scanf("%d%d",&l,&r);
	        q[i].l=r;
	        q[i].r=l+N/2;
	        q[i].pos = i;
	    }
	    sort(q+1,q+1+M,cmp);
	 
	 
	    for(int i=1;i<=N;i++)
	        if(fir[i])//如果这个数是第一次出现 
	        {
	            modify(i,1);//初始化
	        }
	    int qtemp = q[1].l;//当前位置 
	    int ptr = 1;//下标位置 
	    for(int i=1;i<=M;i++)
	    {
	        for(;ptr<q[i].l;ptr++)
	        {
	            if(fir[ptr])//如果第一次出现 
	            {
	                modify(ptr,-1); 
	                fir[ptr] = false;//下标在l之前不在区间内 
	                if(Next[ptr])//如果后面还有重复 
	                {
	                    fir[Next[ptr]] = true;//下一个是第一次出现 
	                    modify(Next[ptr],1);//这个位置的前一个位置次数
	                }
	            }
	        }
	        ptr = q[i].l;
	        qtemp = q[i].l;
	        res[q[i].pos] = sum(q[i].r) - sum(q[i].l-1);
	    }
	    for(int i=1;i<=M;i++) printf("%d\n",res[i]);
	}
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值