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
#include
#include
#include
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