[BZOJ 3585]mex

本文介绍了一种使用莫队算法和分块技术解决区间查询问题的方法,并详细讲解了如何通过分块来优化时间复杂度,包括具体的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

外面一定要套一层莫队的不用说QAQ

学会分析时间复杂度OTZ

按照权值来说,我们需要维护一些数据结构

比如树状数组(查询时二分??)  -----O(msqrt(n)logn)

比如分块(修改O(1)查询(sqrt(n)))-----O(msqrt(n))


这里写了一份分块。

树状数组的话可以按照权值的有无来二分。


#include 
#include 
#include 
#include 
#include 
#define maxn 200010
using namespace std;

int n, m;

int a[maxn], pos[maxn];

struct opt{
	int l, r, id;
	bool operator<(const opt& k)const{
		if(pos[l] != pos[k.l])return pos[l] < pos[k.l];
		return r < k.r;
	}
}q[maxn];

int ans[maxn];

struct block{
	int sum, cnt, l, r, wy;
	int vis[500];
	
	void build(int l, int r){
		sum = r - l + 1;
		this->l = l, this->r = r;
		wy = l - 1;
		memset(vis, 0, sizeof vis);
	}
	
	void modify(int p, int type){
		p -= wy;
		if(type == 1){
			if(!vis[p])cnt ++;
			vis[p] ++;
		}
		else{
			vis[p] --;
			if(!vis[p])cnt --;
		}
	}
	
	bool empty(){return sum == cnt;}
	
	int ask(){
		for(int i = 1; i <= sum; i ++)
		    if(!vis[i])return i + wy;
	}
}b[500];

int main(){
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i ++)
	    scanf("%d", &a[i]);
    int blo = sqrt(n) + 1;
	for(int i = 1; i <= blo; i ++){
		int L = (i - 1) * blo + 1, R = i * blo;R = min(R, n);
		if(i == 1)L = 0;
		if(L > R){blo = i;break;}
		for(int j = L; j <= R; j ++)
			pos[j] = i;
		b[i].build(L, R);
	}
	for(int i = 1; i <= m; i ++){
		scanf("%d%d", &q[i].l, &q[i].r);
		q[i].id = i;
	}
	sort(q + 1, q + 1 + m);
	int l = 1, r = 0;
	for(int i = 1; i <= m; i ++){
		for(; l < q[i].l; l ++){
			if(a[l] > n)continue;
			b[pos[a[l]]].modify(a[l], -1);
		}
		for(; l > q[i].l; l --){
            if(a[l - 1] > n)continue;
			b[pos[a[l - 1]]].modify(a[l - 1], 1);
		}
		for(; r < q[i].r; r ++){
            if(a[r + 1] > n)continue;
			b[pos[a[r + 1]]].modify(a[r + 1], 1);
		}
		for(; r > q[i].r; r --){
            if(a[r] > n)continue;
			b[pos[a[r]]].modify(a[r], -1);
		}
		int ret = 0;
		for(int j = 1; j <= blo; j ++)
		    if(!b[j].empty()){
				ret = b[j].ask();
				break;
		    }
		ans[q[i].id] = ret;
	}
	for(int i = 1; i <= m; i ++)
	    printf("%d\n", ans[i]);
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值