复杂度分析
设块的大小为blocksize,序列总长为n,有m次询问,t次修改。
(1)l的移动:若下一个询问与当前询问在同一个块中则最多移动blocksize步。若下一个询问与当前询问不在同一块中则最多移动n步且移动n步后一定在一个块中。故最多移动
O
(
m
∗
b
l
o
c
k
s
i
z
e
+
n
)
O(m*blocksize+n)
O(m∗blocksize+n)
(2)r的移动:枚举完一个块,r最多移动n次,总共有n/blocksize块,复杂度为
O
(
n
∗
n
/
b
l
o
c
k
s
i
z
e
)
O(n*n/blocksize)
O(n∗n/blocksize)
(3)t的移动:我们要寻找有多少个单调段(一个单调段下来最多移动t次)。当且仅当两个询问l在同块,r也在同块时,才会对t进行排序。那么对于每一个l的块,里面r最坏情况下占据了所有的块,所以最坏情况下:有
n
/
b
l
o
c
k
s
i
z
e
n/blocksize
n/blocksize个l的块,每个l的块中会有
n
/
b
l
o
c
k
s
i
z
e
n/blocksize
n/blocksize个r的块,此时,在一个r块里,就会出现有序的t。所以t的单调段个数最多为:
(
n
/
b
l
o
c
k
s
i
z
e
)
∗
(
n
/
b
l
o
c
k
s
i
z
e
)
(n/blocksize)*(n/blocksize)
(n/blocksize)∗(n/blocksize)。每个单调段最多移动t次,复杂度为
O
(
(
n
/
b
l
o
c
k
s
i
z
e
)
2
∗
t
)
O((n/blocksize)^2*t)
O((n/blocksize)2∗t)。
故总复杂度为
O
(
m
∗
b
l
o
c
k
s
i
z
e
+
n
+
n
∗
n
/
b
l
o
c
k
s
i
z
e
+
(
n
/
b
l
o
c
k
s
i
z
e
)
2
∗
t
)
O(m*blocksize+n+n*n/blocksize+(n/blocksize)^2*t)
O(m∗blocksize+n+n∗n/blocksize+(n/blocksize)2∗t)
当blocksize取
(
n
t
)
1
3
(nt)^\frac{1}{3}
(nt)31是较优
例题
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
int n,m;
int a[N],cnt[10*N]/*数字的范围*/,ans[N];
int blocksize = 1;
struct query{
int l,r,pre,id,bl,br;
}q[N];
struct change{
int pos,pre,cur;
}cg[N];
bool cmp(query a,query b){
if(a.bl != b.bl) return a.bl < b.bl;
if(a.br != b.br) return a.br < b.br;
return a.pre<b.pre;
}
int main(){
scanf("%d%d",&n,&m);
for(int i = 1;i<=n;i++){
scanf("%d",&a[i]);
}
int totq = 0,totcg = 0;
for(int i = 1;i<=m;i++){
char s;
cin >> s;
if(s == 'Q'){
totq++;//cin的时候++是很危险的
scanf("%d%d",&q[totq].l,&q[totq].r);
q[totq].id = totq;
q[totq].pre = totcg;
}
else{
totcg++;
scanf("%d%d",&cg[totcg].pos,&cg[totcg].cur);
cg[totcg].pre = a[cg[totcg].pos];
a[cg[totcg].pos] = cg[totcg].cur;
}
}
blocksize=ceil(exp((log(n)+log(totcg+1/*totcg有可能是0就会变负无穷*/))/3));//统一的块的大小
for(int i = 1;i<=totq;i++){
q[i].bl = (q[i].l)/blocksize;
q[i].br = (q[i].r)/blocksize;
}
sort(q+1,q+1+totq,cmp);
for(int i = totcg;i>=1;i--){
a[cg[i].pos] = cg[i].pre;
}
int l = 1,r = 0,num = 0,t = 0;//l=1,r=0;
for(int i = 1;i<=totq;i++){
int cl = q[i].l,cr = q[i].r,ct = q[i].pre;
while(cl<l) num += !cnt[a[--l]]++;
while(cl>l) num -= !--cnt[a[l++]];
while(cr>r) num += !cnt[a[++r]]++;
while(cr<r) num -= !--cnt[a[r--]];
while(ct<t){
if((cg[t].pos>=l) && (cg[t].pos<=r)){
num -= !--cnt[a[cg[t].pos]];
}
a[cg[t].pos] = cg[t].pre;
if((cg[t].pos>=l) && (cg[t].pos<=r)){
num += !cnt[a[cg[t].pos]]++;
}
t--;
}
while(ct>t){
t++;
if((cg[t].pos>=l) && (cg[t].pos<=r)){
num -= !--cnt[a[cg[t].pos]];
}
a[cg[t].pos] = cg[t].cur;
if((cg[t].pos>=l) && (cg[t].pos<=r)){
num += !cnt[a[cg[t].pos]]++;
}
}
ans[q[i].id] = num;
}
for(int i = 1;i<=totq;i++) printf("%d\n",ans[i]);
return 0;
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5+10;
int cnt[N],sum[N],ans[N],a[N];
int blocksize;
struct query{
int l,r,up,down,bl,br,id;
}q[N];
bool cmp(query a,query b){
if(a.bl != b.bl) return a.bl<b.bl;
return a.br < b.br;
}
void add(int x){
if(++cnt[x] == 1) sum[x/blocksize]++;
}
void del(int x){
if(--cnt[x] == 0) sum[x/blocksize]--;
}
int calc(int x){
int now=0;
for (int i=0;i<x/blocksize;i++) now+=sum[i];
for (int i=(x/blocksize)*blocksize;i<=x;i++) now+=(cnt[i]>=1);
return now;
}
int main(){
ios::sync_with_stdio(false); cin.tie(0);
int T,n,m;
cin >> T;
while(T--){
for(int i = 0;i<=100000;i++){
cnt[i] = 0;
sum[i] = 0;
a[i] = 0;
}
cin >> n >> m;
blocksize = ceil(sqrt(n));
for(int i = 1;i<=n;i++) cin >> a[i];
for(int i = 1;i<=m;i++){
cin >> q[i].l >> q[i].down >> q[i].r >> q[i].up;
q[i].id = i;
q[i].bl = q[i].l/blocksize;
q[i].br = q[i].r/blocksize;
}
sort(q+1,q+1+m,cmp);
int l = 1,r = 0;
for(int i = 1;i<=m;i++){
int cl = q[i].l,cr = q[i].r;
while(cl<l) add(a[--l]);
while(cl>l) del(a[l++]);
while(cr>r) add(a[++r]);
while(cr<r) del(a[r--]);
ans[q[i].id] = calc(q[i].up)-calc(q[i].down-1);
}
for(int i = 1;i<=m;i++) cout << ans[i] << endl;
}
return 0;
}