莫队套分块
P4396 [AHOI2013] 作业
题目翻译:
给定一个长度为 \(n\) 的序列,\(m\) 次询问,每一次给出 \(l,r,a,b\) 及求在区间 \([l,r]\) 间在值域 \([a,b]\) 的所有数的个数,和数的种数。
算法理解:
莫队套分块,显而易见就是在运用莫队的前提下,用分块来处理莫队的增减值。分块的复杂度为 \(m\sqrt n\) 而莫队的复杂度为 \(n \sqrt m\) 因此莫队套分块的复杂度为 \(O(n \sqrt m + m \sqrt n)\),分块的作用就是在增减值时若要枚举大量数据时可以用分块优化
思路:
\(1.\) 分析题目发现,我们可以用莫队来枚举区间,每一次枚举就将对应的值用桶储存下来,最后将要求值域的内的所有个数加上即可(求种数只用加一)。这样的复杂度为\(O(m \sqrt n + maxn\times n)\)(\(maxn\) 指值域最大值)
\(2.\) 考虑优化,既然是用分块,那我们可以将值域分成 \(\sqrt {maxn}\),再用数组储存块的左右端点,这样累加答案时只需要加上块的答案,和多余的单独答案即可,复杂度为 \(O(m\sqrt n+m\sqrt {maxn})\) (对于种数也是同理)
完整代码:
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int block[N],block2[N],a[N],num[N];
int br[N],bl[N];
struct Q{
int l,r,a,b,id;
}q[N];
bool cmp(Q x,Q y){
if(block[x.l]!=block[y.l])return x.l<y.l;
if(block[x.l]&1)return x.r<y.r;
return x.r>y.r;
}
int sum[N],sum1[N];
bool vis[N];
void update(int x,int val){
num[x]+=val;
sum[block2[x]]+=val;
if(num[x]==1 && val==1){
vis[x]=1;
sum1[block2[x]]++;
}
if(num[x]==0 && val==-1){
vis[x]=0;
sum1[block2[x]]--;
}
}
int query(int a,int b){
if(a>b)return 0;
int ans=0;
if(block2[a]==block2[b]){
for(int i=a;i<=b;i++){
if(vis[i])ans+=num[i];
}
return ans;
}
else{
for(int i=a;i<=br[block2[a]];i++){
ans+=num[i];
}
for(int i=bl[block2[b]];i<=b;i++){
ans+=num[i];
}
for(int i=block2[a]+1;i<=block2[b]-1;i++){
ans+=sum[i];
}
return ans;
}
}
int query2(int a,int b){
if(a>b)return 0;
int ans=0;
if(block2[a]==block2[b]){
for(int i=a;i<=b;i++){
if(vis[i])ans++;
}
return ans;
}
else{
for(int i=a;i<=br[block2[a]];i++){
ans+=vis[i];
}
for(int i=bl[block2[b]];i<=b;i++){
ans+=vis[i];
}
for(int i=block2[a]+1;i<=block2[b]-1;i++){
ans+=sum1[i];
}
return ans;
}
}
void add(int x){
update(a[x],1);
}
void del(int x){
update(a[x],-1);
}
int ans1[N],ans2[N];
int main(){
int n,m;
scanf("%d%d",&n,&m);
int len=1.0*n/sqrt(m)+1;
for(int i=1;i<=n;i++){
block[i]=(i-1)/len+1;
}
int maxn=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
maxn=max(maxn,a[i]);
}
len=sqrt(maxn);
for(int i=1;i<=maxn;i++){
block2[i]=(i-1)/len+1;
}
int blocknum=block2[maxn];
for(int i=1;i<=blocknum;i++){
bl[i]=(i-1)*len+1;
br[i]=i*len;
}
blocknum=block2[maxn];
for(int i=1;i<=m;i++){
scanf("%d%d%d%d",&q[i].l,&q[i].r,&q[i].a,&q[i].b);
q[i].b=min(q[i].b,maxn);
q[i].id=i;
}
sort(q+1,q+m+1,cmp);
int l=1,r=0;
for(int i=1;i<=m;i++){
while(l<q[i].l)del(l++);
while(l>q[i].l)add(--l);
while(r<q[i].r)add(++r);
while(r>q[i].r)del(r--);
ans1[q[i].id]=query(q[i].a,q[i].b);
ans2[q[i].id]=query2(q[i].a,q[i].b);
}
for(int i=1;i<=m;i++){
printf("%d %d\n",ans1[i],ans2[i]);
}
}
标签:block2,分块,int,复杂度,sqrt,笔记,莫队
From: https://www.cnblogs.com/XichenOC/p/18682497