2025.7.19 莫队
2025.7.19 莫队
对于序列上的区间询问问题,如果从 \([l,r]\) 的答案能够扩展到 \([l-1,r],[l,r+1]\)(即与 \([l,r]\) 相邻的区间)的答案,那么可以在 \(O(n\sqrt n)\) 的复杂度内求出所有询问的答案。
实现
对询问区间进行排序,减少拓展次数。
一般都是先扩展,后减小。
void add(int x){
//加大区间
}
void del(int x){
//减小区间
}
bool cmp(node a,node b){
if(a.l/d!=b.l/d){
return a.l<b.l;
}
return a.r/d>b.r/d;
}
int main(){
cin>>n>>m;
d=sqrt(n);
for(int i=1;i<=n;i++){
cin>>q[i].l>>q[i].r;
q[i].id=i;
}
sort(q+1,q+m+1,cmp);//对询问区间进行排序
for(int i=1,l=1,r=0;i<=m;i++){
while(l>q[i].l) add(--l);
while(r<q[i].r) add(++r);
while(l<q[i].l) del(l++);
while(r>q[i].r) del(r--);
ans[q[i].id]=sum;
}
}
例题P1903
题目描述
墨墨购买了一套 \(N\) 支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会向你发布如下指令:
- \(Q\) \(l\) \(r\) 代表询问你从第 \(l\) 支画笔到第 \(r\) 支画笔中共有几种不同颜色的画笔。
- \(R\) \(p\) \(c\) 把第 \(p\) 支画笔替换为颜色 \(c\) 。
思路
对于第一种操作,使用莫队即可。但存在第二种操作,每次询问还需记录之前有几次第二种操作。
对于第二种操作,将颜色 \(c\) 与当前颜色交换,因为可能因排序导致第二种操作需要撤回,所以颜色之间是交换而不是覆盖。
AC代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m,d,c[N],cnt[N*5],ans[N],cntq=0,cntr=0,sum=0;
struct node{
int l,r,id,t;
}q[N];
bool cmp (node a,node b){
return a.l/d==b.l/d?a.r/d==b.r/d?a.t<b.t:a.r<b.r:a.l<b.l;
}
struct re{
int p,c;
}re[N];
void add(int x){
sum+=!cnt[x]++;//如果之前未出现,sum++
}
void del(int x){
sum-=!--cnt[x];//如果仅剩一个,sum--
}
void up(int i,int t){
if(q[i].l<=re[t].p&&re[t].p<=q[i].r){//当前区间已经包括p
del(c[re[t].p]);
add(re[t].c);
}
swap(c[re[t].p],re[t].c);//颜色交换
}
int main(){
scanf("%d%d",&n,&m);
d=pow(n,0.666);
for(int i=1;i<=n;i++){
scanf("%d",&c[i]);
}
for(int i=1;i<=m;i++){
char op;
int l,r;
scanf("%s%d%d",&op,&l,&r);
if(op=='Q'){
cntq++;
q[cntq].l=l,q[cntq].r=r,q[cntq].id=cntq;
q[cntq].t=cntr;//记录操作2次数
}
else{
cntr++;
re[cntr].p=l,re[cntr].c=r;
}
}
sort(q+1,q+cntq+1,cmp);//排序
for(int i=1,l=1,r=0,t=0;i<=cntq;i++){
while(l>q[i].l) add(c[--l]);
while(r<q[i].r) add(c[++r]);
while(l<q[i].l) del(c[l++]);
while(r>q[i].r) del(c[r--]);
while(t<q[i].t) up(i,++t);
while(t>q[i].t) up(i,t--);
ans[q[i].id]=sum;
}
for(int i=1;i<=cntq;i++){
printf("%d\n",ans[i]);
}
return 0;
}
完结撒花!!!

浙公网安备 33010602011771号