AcWing 2492. HH的项链
莫队求的是询问给定区间内不同的数字的个数,使用双指针遍历给定的区间,在这种情况下依然会超时,所以就对询问的区间按照分块(第二关键字是区间右端点)进行排序,这样就可以保证双指针不重复遍历,最大程度降低时间复杂度
只是总结一下大概思路,具体讲解还是听y总的吧
#include<bits/stdc++.h>
using namespace std;
const int N = 50010, M = 2e5 + 10, S = 1e6 + 10;
struct query{
int id, l, r;
}q[M]; //记录询问
int n, m;
int w[N]; //记录项链的值
int cnt[S]; //记录每个数出现的次数,能出现的数的范围是1~1e6
int res; //记录不同的数出现过的次数
int len; //一块内元素的最小值
int ans[S];
int get(int x){
return x / len;
}
bool cmp(query const& a, query const& b){
int i = get(a.l), j = get(b.l);
if(i != j) return i < j;
return a.r < b.r;
}
void add(int x, int &res){
cnt[x] ++ ;
if(cnt[x] == 1) res ++ ;
}
void del(int x, int &res){
cnt[x] -- ;
if(!cnt[x]) res -- ;
}
int main()
{
cin>>n;
for(int i = 1; i <= n; i ++ ){
cin>>w[i];
}
cin>>m;
len = max(1.0, sqrt((double)n * n / m));
for(int i = 0; i < m; i ++ ){
int a, b;
cin>>a>>b;
q[i] = {i, a, b};
}
sort(q, q + m, cmp);
for(int k = 0, i = 0, j = 1, res = 0; k < m; k ++ ){ //res是一直要用的数,所以不用每次都重置为1
int id = q[k].id, l = q[k].l, r = q[k].r;
//当r=6,i=3时,要考虑加入区间的数不包括w[3],所以是 ++ i
while(i < r) add(w[ ++ i], res);
//当i=6,r=3时,要考虑删除的数包括w[6],所以是i --
while(i > r) del(w[i -- ], res);
//当j=3,l=6时,要删除的数包括w[3],所以是j ++
while(j < l) del(w[j ++ ], res);
//当j=6,l=3时,要考虑加入区间的数不包括w[6](因为已经在区间内),所以是 ++ i
while(j > l) add(w[ -- j], res);
ans[id] = res;
}
for(int i = 0; i < m; i ++ ) cout<<ans[i]<<endl;
return 0;
}