题目描述
Autumn 和 Bakser 又在研究 Gty 的妹子序列了!但他们遇到了一个难题。
对于一段妹子们,他们想让你帮忙求出这之内美丽度
∈
[
a
,
b
]
\in [a,b]
∈[a,b] 的妹子的美丽度的种类数。
为了方便,我们规定妹子们的美丽度全都在
[
1
,
n
]
[1,n]
[1,n] 中。
给定一个长度为
n
(
1
≤
n
≤
100000
)
n(1\le n\le 100000)
n(1≤n≤100000) 的正整数序列
s
(
1
≤
s
i
≤
n
)
s(1\le s_i\le n)
s(1≤si≤n),对于
m
(
1
≤
m
≤
1000000
)
m(1\le m\le 1000000)
m(1≤m≤1000000) 次询问
l
,
r
,
a
,
b
l,r,a,b
l,r,a,b,每次输出 $s_l\dots s_r中,权值
∈
[
a
,
b
]
\in [a,b]
∈[a,b] 的权值的种类数。
算法分析
莫队算法+分块。
一个显然的思路是莫队之后每次区间变化都使用线段树来维护,然而这样的时间复杂度为 O ( n n l o g 2 n ) O(n\sqrt{n}log_2n) O(nnlog2n),会超时。
我们发现对于一个询问,我们处理部分的时间复杂度为均摊 O ( n l o g 2 n ) O(\sqrt{n}log_2n) O(nlog2n),而计算答案部分的复杂度近为 l o g 2 n log_2n log2n,需要减少处理部分的复杂度,可适当增加计算答案部分的复杂度。
对权值分块,每个权值维护目前区间中其出现次数 c n t cnt cnt,对每块维护块中 c n t cnt cnt 不为 0 0 0 的个数,这样,区间单位变化时的复杂度就是 O ( 1 ) O(1) O(1),区间变化部分的总复杂度为 O ( n ) O(\sqrt{n}) O(n),每次查询部分小块暴力,大块直接查询,时间复杂度为 O ( n ) O(\sqrt{n}) O(n),总时间复杂度为 O ( n n ) O(n\sqrt{n}) O(nn)。
注意分块时要判断左右端点是否在同一个块内,预处理每个块的左右边界以方便处理时要记得初始化。
代码实现
#include <cstdio>
#include <cmath>
#include <algorithm>
#define cmin(x,y) x=std::min(x,y)
#define cmax(x,y) x=std::max(x,y)
const int maxn=100005;
const int maxm=1000005;
struct ask {int id,l,r,a,b;} qs[maxm];
int sz,bl[maxn],le[maxn],ri[maxn];
inline bool cmp(const ask &x,const ask &y) {return bl[x.l]^bl[y.l]?x.l<y.l:bl[x.l]&1?x.r<y.r:x.r>y.r;}
int s[maxn],num[maxn],cnt[maxn],ans[maxm];
int main() {
int n,m;scanf("%d%d",&n,&m);
for(register int i=1;i<=n;++i) scanf("%d",&s[i]);
for(register int i=0;i<m;++i) {qs[i].id=i;scanf("%d%d%d%d",&qs[i].l,&qs[i].r,&qs[i].a,&qs[i].b);}
sz=n/sqrt(m*2/3);for(register int i=1;i<=n;++i) {le[i]=n;ri[i]=0;}
for(register int i=1;i<=n;++i) {bl[i]=(i-1)/sz+1;cmin(le[bl[i]],i);cmax(ri[bl[i]],i);}
std::sort(qs,qs+m,cmp);
for(register int i=0,l=1,r=0;i<m;++i) {
while(l<qs[i].l) {--cnt[s[l]];num[bl[s[l]]]-=(!cnt[s[l]]);++l;}
while(l>qs[i].l) {--l;num[bl[s[l]]]+=(!cnt[s[l]]);++cnt[s[l]];}
while(r<qs[i].r) {++r;num[bl[s[r]]]+=(!cnt[s[r]]);++cnt[s[r]];}
while(r>qs[i].r) {--cnt[s[r]];num[bl[s[r]]]-=(!cnt[s[r]]);--r;}
int &res=ans[qs[i].id];int a=qs[i].a,b=qs[i].b;
if(bl[a]^bl[b]) {
for(register int j=a;j<=ri[bl[a]];++j) if(cnt[j]) ++res;
for(register int j=bl[a]+1;j<=bl[b]-1;++j) res+=num[j];
for(register int j=le[bl[b]];j<=b;++j) if(cnt[j]) ++res;
}
else for(register int j=a;j<=b;++j) if(cnt[j]) ++res;
}
for(register int i=0;i<m;++i) printf("%d\n",ans[i]);
return 0;
}