给定一个长度为
n
n
n (
n
≤
1
e
5
n \leq 1e5
n≤1e5) 的序列
a
n
a_n
an (
a
i
≤
1
e
5
a_i\leq 1e5
ai≤1e5),
m
m
m (
m
≤
1
e
5
m \leq 1e5
m≤1e5) 次询问,每次询问给定 l r a b,求区间
[
l
,
r
]
[l,r]
[l,r] 内大于等于
a
a
a 且小于等于
b
b
b 的数的个数,以及满足条件的不同数字的种数。
思路
容易想到莫队,但是对于区间的统计,如果直接莫队的话,需要使用树状数组 / 线段树等数据结构维护区间,时间复杂度会变成
O
(
n
m
log
N
)
O(n\sqrt m\log N)
O(nmlogN),显然无法接受。
朴素的想法,莫队的每次更新是
O
(
1
)
O(1)
O(1) 的,查询是
O
(
N
)
O(N)
O(N) 的。因为有
n
m
n\sqrt m
nm 次更新操作和
m
m
m 次查询操作,显然更新比查询对复杂度的要求更苛刻。如果我们可以
O
(
1
)
O(1)
O(1) 地更新、
O
(
N
)
O(\sqrt N)
O(N) 地查询,那么复杂度是可以接受的。
值域分块可以做到这一点。由于是单点更新,显然可以在
O
(
1
)
O(1)
O(1) 的时间内完成对单点和块的更新,分块的查询是
O
(
N
)
O(\sqrt N)
O(N) 的,所以总复杂度为
O
(
n
m
+
m
N
)
O(n\sqrt m+m\sqrt N)
O(nm+mN)。
代码
#include<bits/stdc++.h>constexprint N =100000;intmain(){
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);int n, m;
std::cin >> n >> m;
std::vector<int>a(n +1);for(int i =1; i <= n;++i){
std::cin >> a[i];}
std::vector<std::array<int,5>>Q(m);for(int i =0; i < m;++i){
std::cin >> Q[i][0]>> Q[i][1]>> Q[i][2]>> Q[i][3];
Q[i][4]= i;}int len = std::sqrt(n);
std::sort(Q.begin(), Q.end(),[&](auto x,auto y){if((x[0]-1)/ len !=(y[0]-1)/ len)return x[0]< y[0];if((x[0]-1)/ len &1)return x[1]> y[1];return x[1]< y[1];});int siz = std::sqrt(N);int num =(N + siz -1)/ siz;
std::vector<int>st(num +1),ed(num +1),id(N +1);for(int i =1; i <= num;++i){
st[i]= ed[i -1]+1;
ed[i]= std::min(st[i]+ siz -1, N);for(int j = st[i]; j <= ed[i];++j){
id[j]= i;}}
std::vector<std::pair<int,int>>ans(m);
std::vector<int>cnt(N +1),sum(num +1),ap(num +1);auto add =[&](int x){
cnt[a[x]]++;
sum[id[a[x]]]++;if(cnt[a[x]]==1) ap[id[a[x]]]++;};auto del =[&](int x){
cnt[a[x]]--;
sum[id[a[x]]]--;if(cnt[a[x]]==0) ap[id[a[x]]]--;};auto query =[&](int fr,int to)-> std::pair<int,int>{int res1 =0, res2 =0;for(int i = fr; i <= std::min(ed[id[fr]], to);++i){if(cnt[i]) res1 += cnt[i], res2++;}if(id[fr]!= id[to]){for(int i = std::max(fr, st[id[to]]); i <= to;++i){if(cnt[i]) res1 += cnt[i], res2++;}}for(int i = id[fr]+1; i < id[to];++i){
res1 += sum[i];
res2 += ap[i];}return{res1, res2};};int l =1, r =0;for(auto[ql, qr, fr, to, id]: Q){while(l > ql)add(--l);while(r < qr)add(++r);while(l < ql)del(l++);while(r > qr)del(r--);
ans[id]=query(fr, to);}for(auto[x, y]: ans){
std::cout << x <<" "<< y <<"\n";}return0;}