zoukankan      html  css  js  c++  java
  • [BZOJ 3585] mex 【莫队+分块】

    题目链接:BZOJ - 3585

    题目分析

    区间mex,即区间中没有出现的最小自然数。

    那么我们使用一种莫队+分块的做法,使用莫队维护当前区间的每个数字的出现次数。

    然后求mex用分块,将权值分块(显然mex 一定小于等于 n ,大于 n 的权值没有意义,可以直接忽略),每块大小 sqrt(n) 。

    然后区间中的某个数的数量被减到0的时候就将它所在的块的种类计数减一,添加数的时候类似。

    然后枚举每个块,找到最小的中间有数不存在的块(即种类数小于块中的数的种数),然后到这个快里直接从小一个一个找到第一个不存在的数。

    这样莫队的复杂度和分块的复杂度是相加的, O(n^1.5) + O(n^1.5) = O(n^1.5) 。

    代码

    #include <iostream>
    #include <cstdlib>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    
    using namespace std;
    
    const int MaxN = 200000 + 5, MaxQ = 200000 + 5, MaxB = 500 + 5;
    
    int n, m, BlkSize, MaxBlk;
    int A[MaxN], Blk[MaxN], L[MaxB], R[MaxB], Ans[MaxQ], Cnt[MaxN], V[MaxB], Size[MaxB];
    
    struct Query
    {
    	int Idx, l, r, v;
    } Q[MaxQ];
    
    inline bool Cmp(Query q1, Query q2)
    {
    	if (q1.v == q2.v) return q1.r < q2.r;
    	return q1.v < q2.v;
    }
    
    inline void Del_Num(int x)
    {
    	--Cnt[x];
    	if (Cnt[x] == 0) --V[Blk[x]];
    }
    
    inline void Add_Num(int x)
    {
    	if (Cnt[x] == 0) ++V[Blk[x]];
    	++Cnt[x];
    }
    
    void Pull(int f, int x, int y)
    {
    	if (x == y) return;
    	if (f == 0)
    	{
    		if (x < y)
    		{
    			for (int i = x; i < y; ++i)
    				Del_Num(A[i]);
    		}
    		else
    		{
    			for (int i = x - 1; i >= y; --i)
    				Add_Num(A[i]);
    		}
    	}
    	else
    	{
    		if (x < y) 
    		{
    			for (int i = x + 1; i <= y; ++i)
    				Add_Num(A[i]);
    		}
    		else
    		{
    			for (int i = x; i > y; --i)
    				Del_Num(A[i]);
    		}
    	}
    }
    
    int Get_Ans()
    {
    	int ret = n;
    	for (int i = 1; i <= MaxBlk; ++i)
    		if (V[i] < Size[i])
    		{
    			for (int j = L[i]; j <= R[i]; ++j)
    				if (Cnt[j] == 0)
    				{
    					ret = j;
    					break;
    				}
    			break;
    		}
    	return ret;
    }
    
    int main()
    {
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i <= n; ++i)
    	{
    		scanf("%d", &A[i]);
    		if (A[i] > n) A[i] = n + 1;
    	}
    	BlkSize = (int)sqrt((double)n);
    	for (int i = 0; i <= n; ++i) Blk[i] = i / BlkSize + 1;
    	MaxBlk = Blk[n];
    	for (int i = 1; i <= MaxBlk; ++i)
    	{
    		L[i] = (i - 1) * BlkSize;
    		R[i] = i * BlkSize - 1;
    		Size[i] = R[i] - L[i] + 1;
    	}
    	R[MaxBlk] = n;
    	Size[MaxBlk] = n - L[MaxBlk] + 1;
    	for (int i = 1; i <= m; ++i)
    	{
    		scanf("%d%d", &Q[i].l, &Q[i].r);
    		Q[i].Idx = i;
    		Q[i].v = Q[i].l / BlkSize;
    	}
    	sort(Q + 1, Q + m + 1, Cmp);
    	for (int i = Q[1].l; i <= Q[1].r; ++i) Add_Num(A[i]);
    	Ans[Q[1].Idx] = Get_Ans();
    	for (int i = 2; i <= m; ++i)
    	{
    		if (Q[i].r <= Q[i - 1].l) 
    		{
    			Pull(0, Q[i - 1].l, Q[i].l);
    			Pull(1, Q[i - 1].r, Q[i].r);
    		}
    		else
    		{
    			Pull(1, Q[i - 1].r, Q[i].r);
    			Pull(0, Q[i - 1].l, Q[i].l);
    		}
    		Ans[Q[i].Idx] = Get_Ans();
    	}
    	for (int i = 1; i <= m; ++i) printf("%d
    ", Ans[i]);
    	return 0;
    }
    

      

  • 相关阅读:
    直面焦虑烦恼 谈谈成长
    Makefile入门1
    递归
    极客时间的专栏
    作者介绍
    1.试除法判定质数 2.分解质因数 质数
    17.没有上司的舞会 树形DP
    17.二分图的最大匹配
    16.染色法判定二分图
    15.Kruskal算法求最小生成树
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4422711.html
Copyright © 2011-2022 走看看