Dễ dàng nhận thấy rằng ta có thể dùng thuật toán Mo's và Fenwick tree hoặc Segment tree để giải bài toán này trong O((n+q)√nlogn)
Vậy thì làm thế nào để ta có thể tối ưu nó hơn? Gọi freq(i) là tần số xuất hiện của phần tử i trong đoạn ta đang xét, ta cũng có thể dễ dàng tính mảng distinct(i) là số phần tử có freq(i)>0. Ta có thể tính nhanh bằng cách dịch đoạn bằng Mo's với mỗi truy vấn trong O(B)
Vậy vấn đề cuối cùng ở đây là làm sao để tính nhanh số phần tử có trong đoạn / số phần tử phân biệt?
Ta có thể chia mảng freq và distinct ra thành B đoạn. Gọi f(i) là tổng của freq(j), và d(i) là tổng của distinct(j) với j∈[i∗B,(i+1)∗B)
Sau khi xử lý xong, với mỗi truy vấn, ta chỉ đơn giản là xét các đoạn i sao cho [i∗B,(i+1)∗B)∈[a,b] và xử lý đoạn không nằm trong hoàn toàn riêng. Khi ta chọn độ dài của đoạn B=√n thì tổng độ phức tạp sẽ là O((n+q)∗2√n)
Code tham khảo:
cpp#∈cludebits/stdc++.husingnamespacestd;const∫N=1e5;const∫sqr=320;structQuery{∫l,r,a,b,;pair<∫,∫>→Pair()const{∫blk=lsqr;return{blk,blk&1?-r:r};}b∞lopera→r<(constQuery&other){return→Pair()<other.→Pair();}};∫ma∈(){c∈.tie(0)→syncwithstdio(0);∫n,q;c∈〉n〉q;→→r<∫>v(n);for(∫i=0;i<n;i++){c∈〉v[i];}array<∫,N+1>eq{};array<→→r<∫>,2>cnt;cnt[0]=cnt[1]=→→r<∫>(Nsqr+1);au→add=[&](∫x){∫blk=xsqr;cnt[0][blk]++;cnt[1][blk]+=(eq[x]==0);eq[x]++;};au→∂=[&](∫x){∫blk=xsqr;cnt[0][blk]--;cnt[1][blk]≡(eq[x]==1);eq[x]--;};→→r<Query>queries(q);for(∫i=0;i<q;i++){∫l,r,a,b;c∈〉l〉r〉a〉b;--l;--r;queries[i]={l,r,a,b,i};}sort(queries.beg∈(),queries.end());→→r<pair<∫,∫〉ans(q);∫curl=0,curr=-1;for(Query&qr:queries){whi≤(curl>qr.l)add(v[--curl]);whi≤(curr<qr.r)add(v[++curr]);whi≤(curl<qr.l)∂(v[curl++]);whi≤(curr>qr.r)∂(v[curr--]);au→&[u,v]=ans[qr.id