对于区间查询的问题,提供一种思路:

莫队。

莫队是处理区间问题的乱搞神器,尤其是对于离线查询问题,当然也可以做在线查询,比如带修莫队。

对于有的题,莫队是乱搞骗分,而在某些地方,莫队是正解。

这道题来说,可以当做是萌新初学莫队的一个板子,而且莫队也好理

解。线段树树状数组这类也可以做,但是相比莫队而言麻烦些。(个

人见解,不喜勿喷。谢谢)


1st.关于莫队的思想:

先明白一点,莫队可以理解成:

优雅的暴力。

暴力算法几乎人人都会,所以莫队理解起来好理解。

如果让你暴力的话呢?

我们用一个cnt[ ]数组记录每种元素,用桶排的思想,枚举区间,每遇到一个元素对应的桶++,然后暴力一遍所有的桶,等于1的我们ans就++,这样统计不同的个数,看看是不是等于L到R,然后再清空桶和ans,做下一组询问。

对于一般的暴力算法往往会TLE,那么莫队是怎么做的呢?

首先:考虑我们有两个指针。一个叫做curL,另一个叫curR。他们对应的是所指的数的标号。这样我们可以利用这两个指针进行移动,每次只能向左或向右移动一步。移动的复杂度是O(1)。

eg:

我们现在处理了curL—curR区间内的数据,现在左右移动,比如curL到curL-1,只需要更新上一个新的3,即curL-1。

那么curL到curL+1,我们只需要去除掉当前curL的值。因为curL+1是已经维护好了的。

curR同理,但是要注意方向哦!curR到curR+1是更新,curR到cur-1是去除。

我们先计算一个区间 [curL curR] 的answer,这样的话,我们就可以用O(1)转移到 [curL-1 curR] [curL+1 curR] [curL curR+1] [curL curR-1] 上来并且求出这些区间的answer。

我们利用curL和curR,就可以移动到我们所需要求的[L R]上啦~

这样做会快很多,但是......

如果有个**数据,让你在每个L和R间来回跑,而且跨度很大呢??

我们每次只动一步,岂不是又T了??

但是这其实就是莫队算法的核心了。我们的莫队算法还有优化。

这就是莫队算法最精明的地方(我认为的qwq)

我们想,因为每次查询是离线的,所以我们先给每次的查询排一个序。

排序的方法是

分块。

我们把所有的元素分成多个块(即分块)。分了块跑的会更快。再按照右端点从小到大,左端点块编号相同按右端点从小到大。

为什么要这么排序呢?

如果不是按照分块排序,那么一种直观的办法是按照左端点排序,再按照右端点排序。但是这样的表现不好。特别是面对精心设计的数据,这样方法表现得很差。

举个栗子,有6个询问如下:(1, 100), (2, 2), (3, 99), (4, 4), (5, 102), (6, 7)。

这个数据已经按照左端点排序了。用上述方法处理时,左端点会移动6次,右端点会移动移动98+97+95+98+95=483次。

其实我们稍微改变一下询问处理的顺序就能做得更好:(2, 2), (4, 4), (6, 7), (5, 102), (3, 99), (1, 100)。

左端点移动次数为2+2+1+2+2=9次,比原来稍多。右端点移动次数为2+3+95+3+1=104,右端点的移动次数大大降低了。

上面的过程启发我们:我们不应该严格按照升序排序,而是根据需要灵活一点的排序方法

这样,不断地移动端点指针+精妙的排序,就是普通莫队的思想啦~


2ec.Code

1.对于每组查询的记录和排序:

l,r为左右区间编号,p是第几组查询的编号(记录下来为了排序后不打乱顺序还按照原查询的顺序输出),bl是分块数。

struct query{
int l, r, p;
}e[maxn]; bool cmp(query a, query b)
{
return (a.l/bl) == (b.l/bl) ? a.r < b.r : a.l < b.l;
}

2.处理和初始变量:

answer就是所求答案, bl是分块数量, a[]是原序列, ans[]是记录原查询序列下的答案, cnt[]是记录对于每个数i, cnt[i]表示i出现过的次数, curL和curR不再解释, nm看题意要求。 add和del每个题有不同的写法。

int answer, a[maxn], m, n, bl, ans[maxn], cnt[maxn], k, curL = 1, curR = 0;
void add(int pos)//添加
{
//do sth...
}
void del(int pos)//去除
{
//do sth...
}
//一般写法都是边处理 边根据处理求答案。cnt[a[pos]]就是在pos位置上原序列a出现的次数。

3.主体部分及输出:

预处理查询编号,用四个while移动指针顺便处理。

在这里着重说下四个while

我们设想有一条数轴:

当curL < L 时,我们当前curL是已经处理好的了。所以del时先去除当前curL再++

当curL > L 时,我们当前curL是已经处理好的了。所以 add 时先 -- 再加上改后的curL

当curR > R 时,我们当前curR是已经处理好的了。所以del时先去除当前curR再 --

当curR < R 时,我们当前curR是已经处理好的了。所以 add 时先++再加上改后的curR

n = read(); m = read(); k = read();
bl = sqrt(n); for(int i = 1; i <= n; i++)
a[i] = read(); for(int i = 1; i <= m; i++)
{
e[i].l = read(); e[i].r = read();
e[i].p = i;
} sort(e+1,e+1+m,cmp); for(int i = 1; i <= m; i++)
{
int L = e[i].l, R = e[i].r;
while(curL < L)
del(curL++);
while(curL > L)
add(--curL);
while(curR > R)
del(curR--);
while(curR < R)
add(++curR);
ans[e[i].p] = answer;
}
for(int i = 1; i <= m; i++)
printf("%d\n",ans[i]);
return 0;

3th.关于这题题解

我的想法还是蛮暴力的,对于出现过的数直接记录下来,answer记录出现了多少个不同的数,如果answer等于现在的R-L+1,那么说明出现的数与L到R相同。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#define ri register
using namespace std;
const int maxn = 100010;
inline int read()
{
int k=0;
char c;
c=getchar();
while(!isdigit(c))c=getchar();
while(isdigit(c)){k=(k<<3)+(k<<1)+c-'0';c=getchar();}
return k;
}
int n, m, bl, answer = 0, curL, curR, cnt[maxn], a[maxn];
bool ans[maxn];
struct Query{
int l, r, p;
}q[maxn];
bool cmp(const Query &a, const Query &b)
{
return (a.l/bl) == (b.l/bl) ? a.r < b.r : a.l < b.l;
}
void add(int pos)
{
if((++cnt[a[pos]]) == 1) ++answer;
}
void del(int pos)
{
if((--cnt[a[pos]]) == 0) --answer;
}
int main()
{
n = read();
m = read();
bl = sqrt(n);
for(ri int i = 1; i <= n; i++)
a[i] = read(); for(ri int i = 1; i <= m; i++)
{
q[i].l = read();
q[i].r = read();
q[i].p = i;
} sort(q+1,q+1+m,cmp); for(ri int i = 1; i <= m; i++)
{
int L = q[i].l, R = q[i].r;
while(curL < L) del(curL++);
while(curL > L) add(--curL);
while(curR < R) add(++curR);
while(curR > R) del(curR--);
if(answer == (R-L+1))
ans[q[i].p] = 1;
}
for(ri int i = 1; i <= m; i++)
{
if(ans[i] == 1)
printf("Yes\n");
else
printf("No\n");
}
return 0;
}

如果对于莫队算法有什么疑问或者对我的题解有什么建议欢迎和我讨论,本人会尽力解答和虚心接受意见的。

【luogu P3901 数列找不同】 题解的更多相关文章

  1. Luogu P3901 数列找不同

    由于技术原因,题目我贴不上了,大家点下面的链接自己去看吧^_^ P3901 数列找不同 这题第一眼看去,题面真短,有坑(flag) 在往下面看去,woc数据这么大,你要怎样. 现在一起想想想,超级侦探 ...

  2. 【题解】Luogu P3901 数列找不同

    我博客中对莫队的详细介绍 原题传送门 不错的莫队练手题 块数就直接取sqrt(n) 对所有询问进行排序 排序第一关键词:l所在第几块,第二关键词:r的位置 考虑Ai不大,暴力开数组 add时如果加之后 ...

  3. P3901 数列找不同

    P3901 数列找不同 题目描述 现有数列 \(A_1,A_2,\cdots,A_N\) ,Q 个询问 \((L_i,R_i)\) , \(A_{Li} ,A_{Li+1},\cdots,A_{Ri} ...

  4. 【刷题】洛谷 P3901 数列找不同

    题目描述 现有数列 \(A_1,A_2,\cdots,A_N\) ,Q 个询问 \((L_i,R_i)\) , \(A_{Li} ,A_{Li+1},\cdots,A_{Ri}\) 是否互不相同 输入 ...

  5. 洛谷 P3901 数列找不同(莫队)

    题目链接:https://www.luogu.com.cn/problem/P3901 这道题简单莫队模板题,然后$add$和$del$分别处理$vis[]$从$0-->1$和从$1--> ...

  6. 洛谷P3901 数列找不同 [莫队]

    题目传送门 题目描述 现有数列 A_1,A_2,\cdots,A_NA1​,A2​,⋯,AN​ ,Q 个询问 (L_i,R_i)(Li​,Ri​) , A_{Li} ,A_{Li+1},\cdots, ...

  7. 洛谷P3901 数列找不同(莫队)

    传送门 我不管我不管我就是要用莫队 直接用莫队裸上 //minamoto #include<iostream> #include<cstdio> #include<alg ...

  8. 洛谷P3901 数列找不同(莫队水题)

    重温下手感,判断区间是否全是不同的数字有两种做法,一个长度为len的区间不同的数字,参见HH的项链,一种是区间众数,参见蒲公英,是水题没错了.明天搞数据库,然后继续自己的gre和训练计划 #inclu ...

  9. [Luogu 3398] 仓鼠找sugar

    [Luogu 3398] 仓鼠找sugar 又是 LCA- 前两天死活写不过的一个题今天终于顺手切了. 思路嘛参考了一楼题解. 就是说,对于 a, b, c, d 四个点, 令 x = LCA(a, ...

随机推荐

  1. 安卓获取输入法高度与ViewTreeObserver讲解

    目录 安卓获取输入法高度 前言 清单 开始 ViewTreeObserver讲解 获取输入法高度原理 思路 实现 关于ViewTreeObserver 定义 继承 摘要 获取View高度的三种方法 源 ...

  2. Android 软件自动更新功能实现的方法

    相信所有的用户都遇到过软件提醒更新的情况,下面就将实现此功能 首先看一下程序目录结构 步骤: 1.新建一个类UpdateManger,用于显示提示更新 详细出处参考:http://www.jb51.n ...

  3. android中的Touch研究

    android中的事件类型分为按键事件和屏幕触摸事件,Touch事件是屏幕触摸事件的基础事件,有必要对它进行深入的了解. 一个最简单的屏幕触摸动作触发了一系列Touch事件:ACTION_DOWN-& ...

  4. 突破Http协议

    突破Http协议 我到不先说什么Http什么的,对于HTTP的彻底理解是http是应用层的一个程序,就像我们写的诸多客户端和服务器模型,我们可能为了可靠,为了方便数据的解析,我们在数据包中其实就是结构 ...

  5. ObjectHeader、ObjectType和ObjectHook的学习

    0x01 前言 之前研究RootKit技术,发现了对象钩子这个概念,一直不知道是什么,然后在网上搜,最先找到的是sudami的一篇文章,于是跟着大牛的脚步研究,其中也参考<内核情景分析>, ...

  6. HDU 1257——最少拦截系统——————【LIS变型题】

    最少拦截系统 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Statu ...

  7. Windows server 搭建ftp服务器

    1.安装ftp 2.端口端口21和20的入出端口 3.点击IIS 服务器证书 4.FTP界面上选择“FTP身份验证”——>“基本身份验证”–>”启用” 5.FTP界面选择 “FTP授权规则 ...

  8. Android4.4源码学习笔记

    1.StatusBar和Recents是如何建立联系的 在BaseStatusBar的start()函数通过getComponent(RecentsComponent.class)得到了Recents ...

  9. Canvas知识点汇总

    本文主要记录Canvas基础知识汇总. 1.Canvas定义 <canvas> 元素是HTML5中的新元素,通过它可以在网页中绘制出所需的图形.<canvas>标签只是图形的容 ...

  10. 【数据库】5.0 MySQL入门学习(五)——MySQL源码了解及MySQL初始化设置

    1.0 MySQL源码目录主要包括:客户端代码.服务端代码.测试工具.其他库文件.当然,看懂源代码得有一定的C语言基础. BUILD:各种平台的编译脚本,可以用来制作各平台的二进制版本 client: ...