题目链接:Click here~~
题意:
给一个长度为 n 的序列,Q 次询问,每次查询 [l,r] 中有多少个不同的数字,且是否存在一个数字,其出现的位置成等差数列。
解题思路:
接上篇继续刷离线查询题目。昨天想了1天,今天早上在床上想出思路了,2333333。
对于区间中不同数字的个数,可以用类似上篇的做法,c[j] 维护 [j,i] 中不同数字的个数,那么每次在位置 i 出现一个 v ,只需 [last_pos_v+1,i] 的值加 1 。
对于是否有数字的位置成等差数列,就不太好想,其实还可以用类似的方法搞。
cc[j] 维护 [j,i] 中位置成等差数列的数字的个数,每次在位置 i 出现一个 v,那么一定可以将区间 [last_pos_v+1,i] 的值加 1。
再快速找出 v 向前能延伸到的 满足位置成等差数列的 最远位置,如果这个位置比 v 上一次延伸到的位置近,那么给那段相应的区间的值减 1 就行了。
#include <vector>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
struct BIT
{
int c[N];
void init(){
memset(c,0,sizeof(c));
}
int lowbit(int x){
return x & -x;
}
void add(int loc,int val){
while(loc < N){
c[loc] += val;
loc += lowbit(loc);
}
}
void add(int a,int b,int val){
if(a > b)
return ;
add(a,val);
add(b+1,-val);
}
int sum(int loc){
int ret = 0;
while(loc){
ret += c[loc];
loc -= lowbit(loc);
}
return ret;
}
}T1,T2;
struct QAQ
{
int l,r,id;
QAQ(){}
QAQ(int l,int r,int i):l(l),r(r),id(i){}
bool operator < (const QAQ& B) const{
return r < B.r;
}
}query[N];
int a[N],extend[N],ans[N];
vector<int> pos[N];
int main()
{
int n,Q;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<N;i++)
pos[i].push_back(0);
scanf("%d",&Q);
for(int i=0;i<Q;i++)
{
int l,r;
scanf("%d%d",&l,&r);
query[i] = QAQ(l,r,i);
}
sort(query,query+Q);
int j = 0;
for(int i=1;j<Q && i<=n;i++)
{
int v = a[i];
pos[v].push_back(i);
int p = (int)pos[v].size() - 1;
T2.add(pos[v][p-1]+1,i,1);
if(p == 1)
extend[i] = 1;
else
{
if(p == 2 || pos[v][p] - pos[v][p-1] == pos[v][p-1] - pos[v][p-2])
extend[i] = extend[ pos[v][p-1] ];
else
{
extend[i] = pos[v][p-2] + 1;
T2.add(extend[ pos[v][p-1] ],extend[i]-1,-1);
}
}
T1.add(pos[v][p-1]+1,i,1);
while(j < Q && query[j].r == i)
{
ans[ query[j].id ] = T1.sum(query[j].l) + ( T2.sum(query[j].l) ? 0 : 1);
j++;
}
}
for(int i=0;i<Q;i++)
printf("%d\n",ans[i]);
return 0;
}