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号
浙公网安备 33010602011771号