题目链接: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; }