首页 > 其他分享 >莫队

莫队

时间:2024-04-19 20:33:05浏览次数:12  
标签:Sub int while ++ Add include 莫队

莫队

介绍

  • 利用分块进行处理的离线算法;

  • 时间复杂度普遍为 \(O(n\sqrt{n})\) ;

    但会被卡

实现思路

对答案的查询

  • 在已知区间的情况下暴力寻找的目标区间的贡献;

  • 对于已知当前 \([L,R]\) 区间,一共有 \(4\) 种情况:

    1. 加上当前区间左边一格的贡献:

      Add(--L);

      先将当前的下标移至目标格,然后加上当前格的贡献

    2. 加上当前区间右边一格的贡献:

      Add(++R);

      同理

    3. 减去当前区间最左一格的贡献:

      Sub(L++);

      先减去当前下标的贡献,然后将下标移至目标格

    4. 减去当前区间最右一格的贡献:

      Sub(R--);

      同理

  • 对于需要用到的函数 \(Add\) 和 \(Sub\) 分别为:

    • \(Add(x)\) :按照需求算上第 \(x\) 个元素的贡献;
    • \(Sub(x)\) :按照需求去除第 \(x\) 个元素的贡献;

分块的优化

如果仅仅只按上面的思路进行对答案的查询,当多次询问依次为:

​ \([1,2],[n-1,n],[1,2],[n-1,n]...\) 时,复杂度便成了 \(O(mn)\) ...

因此,我们需要优化对询问的查询顺序(这就是离线的原因)

  • 将整体的区间分块,块大小为 \(\sqrt{n}\) ,然后再对询问进行排序,进行查找;

  • 对查询操作排序时:

    • 对于左端点 \(L\) 不在同一块的查询,按照 \(L\) 所在块进行排序;
    • 对于左端点 \(L\) 在同一块的查询,按照 \(R\) 的大小排序;
  • 在排序时保留询问的标号,最后按原顺序输出答案;

代码实现

\(Add\) 和 \(Sub\) 函数的实现需要具体按题目要求来。

定义分块

  • 在输入元素时,需要对元素进行分块:

    int n=read();	//输入元素的个数n
    int siz=sqrt(n);//计算块的大小
    for(int i=1;i<=n;i++){
    	a[i]=read();//输入n个元素
    	pos[i]=i/siz;//计算第i个元素所属的块 并储存在pos[i]里
    }
    

    在这里的分块会在后面对询问的排序中用到;

处理查询

  • 定义结构体:

    struct Q{
    	int l,r,k;
    	bool operator<(Q x)const{
            //处理规则如上
    		if(pos[l]==pos[x.l])
    		return r<x.r;
    		return pos[l]<pos[x.l];
    	}
    }q[N];
    /*
    当然,也可以去掉bool operator一句
    用手打排序函数来代替,例如:
    bool cmp(Q x,Q y){
    	if(pos[x.l]==pos[y.l])
    	return x.r<y.r;
    	return pos[x.l]<pos[y.l];
    }
    在主函数中的sort加上“cmp”函数即可
    */
    
  • 进行处理:

    for(int i=1;i<=m;i++){
        //处理区间为[l,r]的查询,询问编号为k
    	q[i].l=read(),q[i].r=read();
    	q[i].k=i;
    }
    sort(q+1,q+m+1);
    

查找答案

  • int l=1,r=0;
    /*
    由于处理的是[l,r]的闭区间
    因此空集应表示为l=r+1;
    不然集合中会存在一个元素
    例如:
    	l=1,r=1时
    	集合为[1,1],存在a[1]这个元素
    */
    for(int i=1;i<=m;i++){
        //处理情况如上
    	while(q[i].l<l)Add(--l);
    	while(q[i].r>r)Add(++r);
    	while(q[i].l>l)Sub(l++);
    	while(q[i].r<r)Sub(r--);
        //由于实际查询的顺序与输入输出不符
        //因此需要改变输出顺序
    	ans[q[i].k]=res;
    }
    

例题

P2709 小B的询问

luogu传送门:题目链接

  • 代码:

    #include<algorithm>
    #include<iostream>
    #include<string.h>
    #include<stdio.h>
    #include<string>
    #include<vector>
    #include<math.h>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    using namespace std;
    int read(){
    	int s=0,f=1;
    	char _z=getchar();
    	while(_z<'0'||_z>'9'){
    		if(_z=='-')
    		f=-1;
    		_z=getchar();
    	}
    	while(_z>='0'&&_z<='9'){
    		s=(s<<3)+(s<<1)+(_z-'0');
    		_z=getchar();
    	}
    	return s*f;
    }
    const int N=5e4+10;
    typedef long long ll;
    int a[N],pos[N],tot[N];
    ll res=0,ans[N];
    struct Q{
    	int l,r,k;
    	bool operator<(Q x){
    		if(pos[l]==pos[x.l])
    		return r<x.r;
    		return pos[l]<pos[x.l];
    	}
    }q[N];
    void Add(int x){
    	x=a[x];
    	res-=tot[x]*tot[x];
    	tot[x]++;
    	res+=tot[x]*tot[x];
    	return;
    }
    void Sub(int x){
    	x=a[x];
    	res-=tot[x]*tot[x];
    	tot[x]--;
    	res+=tot[x]*tot[x];
    	return;
    }
    int main(){
    	int n=read(),m=read();
    	read();
    	int siz=sqrt(n);
    	for(int i=1;i<=n;i++){
    		a[i]=read();
    		pos[i]=i/siz;
    	}
    	for(int i=1;i<=m;i++){
    		q[i].l=read(),q[i].r=read();
    		q[i].k=i;
    	}
    	sort(q+1,q+m+1);
    	int l=1,r=0;
    	for(int i=1;i<=m;i++){
    		while(q[i].l<l)Add(--l);
    		while(q[i].r>r)Add(++r);
    		while(q[i].l>l)Sub(l++);
    		while(q[i].r<r)Sub(r--);
    		ans[q[i].k]=res;
    	}
    	for(int i=1;i<=m;i++){
    		printf("%lld\n",ans[i]);
    	}
    	return 0;
    }
    

标签:Sub,int,while,++,Add,include,莫队
From: https://www.cnblogs.com/liuqichen121/p/18146727

相关文章

  • 莫队
    简介莫队是一种离线求区间信息的算法,可以做到\(O((N+Q)\cdot\sqrt{N})\)。莫队中使用了分块的思想。首先考虑一个问题:给定一个长度为\(N\)序列\(A\)和\(Q\)次询问,每次询问查询区间\([X_i,Y_i]\)的和。(请先忘掉前缀和、线段树、树状数组什么的)比如以下数据:54322......
  • 莫队
    莫队是一种对于询问做转移的算法。对于可以离线,运算可逆的题目。如果按题目给的顺序操作,可以被以下数据hack11nn11nn11nn11nn...时间复杂度\(O(n^2)\)。我们可以通过某一些排序来降低时间复杂度。首先,把这个序列分成\(\sqrt{n}\)块,每一块按右......
  • C116 莫队二次离线 P4887 莫队二次离线
    视频链接:     LuoguP4887【模板】莫队二次离线(第十四分块(前体))//莫队二次离线O(n*sqrt(n)+n*C(k,14))#include<iostream>#include<cstring>#include<algorithm>#include<cmath>#include<vector>usingnamespacestd;constintN=100005;......
  • 莫队
    查询区间每个数出现的个数,离线算法,O(nsqrt(n))一、普通莫队题目链接https://www.luogu.com.cn/problem/P2709题目大意题目代码#include<iostream>#include<algorithm>#include<cmath>#definelllonglongconstintN=5e4+5;usingnamespacestd;intn,m,k,block......
  • 莫队学习笔记
    目录普通莫队初遇——从暴力谈起困境——乱跑的指针优化——顺路而为之带修莫队参考资料普通莫队初遇——从暴力谈起我们通过一道例题来讨论普通莫队。题目链接。观察数据范围,一个很直接的想法是:开一个数组\(cnt\),\(cnt_i\)表示在询问的区间内数字\(i\)出现的次数。对于......
  • #莫队二次离线,根号分治#洛谷 5398 [Ynoi2018] GOSICK
    题目\(m\)组询问求\(\sum_{l\leqi,j\leqr}[a_i\bmoda_j==0],n,m,a_i\leq5\times10^5\)分析设\(f(l,r,x)\)表示\(i或j\in[1,x],i或j\in[l,r]\)时的答案,\(g_x\)表示\([1,x]\)的答案,根号的做法可以通过三秒由于涉及区间内的求值,需要在莫队的基础上二次离线,那......
  • C114 回滚莫队 歴史の研究
    视频链接:C114回滚莫队歴史の研究_哔哩哔哩_bilibili   LuoguAT_joisc2014_c歴史の研究//回滚莫队O(n^(3/2))#include<iostream>#include<cstring>#include<algorithm>#include<cmath>usingnamespacestd;#defineLLlonglongconstintN=1000005;......
  • 分块与莫队
    不沾树的博客变短了好多。分块例题这道题显然可以使用线段树乱搞过去,不过为了给主角面子我们假设我们不会做。对于一些难以使用数据结构维护答案的序列问题,我们考虑暴力。但是暴力太慢了,于是人们提出了分块。分块,就是把序列分成许多的小段,通过一些神秘的处理实现优化暴力。......
  • C113 带修莫队 P1903 [国家集训队] 数颜色/维护队列
    视频链接:   LuoguP1903[国家集训队]数颜色/维护队列//带修莫队O(n^(5/3))#include<iostream>#include<cstring>#include<algorithm>#include<cmath>usingnamespacestd;constintN=1000005;intn,m,B,mq,mr,a[N];intsum,cnt[N],ans[N];st......
  • C112 莫队算法 P1494 [国家集训队] 小 Z 的袜子
    视频链接:  LuoguP1494[国家集训队]小Z的袜子//普通莫队O(n*sqrt(n))#include<iostream>#include<cstring>#include<algorithm>#include<cmath>usingnamespacestd;constintN=50005;intn,m,B,a[N];intsum,cnt[N],ans1[N],ans2[N];str......