今天机房讲了莫队。

但是蒟蒻我并没有听懂,所以晚上回家恶补,才弄明白莫队。

莫队是莫涛大神发明的,它的作用就是用优秀的复杂度求解于一些区间之间的操作,莫队其实就是一个优雅的暴力,它的复杂度是O(n sqrt(n));

以此为例,其实这题和这题是一样的,不过P1972会卡莫队。

回到此题:

题意很直白,我就不多赘述;

思路1:

暴力,这题最朴素的作法无非就是从l到r扫一遍,用数组记下出现的数,然后求值:

#include<bits/stdc++.h>
using namespace std;
#define maxn 100010
bool flag[maxn];
int n, m, a[maxn], sum, ans, l, r, x, y;
int main()
{
//freopen("count.in","r",stdin);
//freopen("count.out","w",stdout);
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++ i)
scanf("%d", &a[i]);
for(int i = 1; i <= m; ++ i)
{
scanf("%d%d%d%d", &l, &r, &x, &y);
memset(flag, 0, sizeof(flag));
sum = ans = 0;
for(int j = l; j <= r; ++ j)
if(a[j] >= x && a[j] <= y)
{
++ sum;
if(flag[a[j]] == 0)
{
flag[a[j]] = 1;
++ ans;
}
}
printf("%d %d\n", sum, ans);
}
return 0;
}

ans即为所求;

但是这种作法的复杂度是O(n*n)的,肯定过不了;

那么就需要一些优化:

我们用两个指针不断的移动到相应的区间;

int l = 0, r = 1;

然后在移动的时候,我们就不停的add,del;

如果要想右,就add;

否则,就del;

void add(int x)
{
if(cnt[a[x]] == 0)
++ now;
++ cnt[a[x]];
}
void del(int x)
{
-- cnt[a[x]];
if(cnt[a[x]] == 0)
-- now;
}
while(l < q[i].l)
del(l ++);
while(l > q[i].l)
add(-- l);
while(r < q[i].r)
add(++ r);
while(r > q[i].r)
del(r --);

就是上面这样,

这就是莫队?

不,还有一个地方,

我们来假设要查询的区间为[1, 10000000]呢?

那不一样还是没有优化。

所以莫队还要一个很重要的地方,就是排序;

该如何排序?

如果按左端点排序,那么右端点就会不好表示;

如果按右端点排序,那么左端点就会不好表示;

这个时候,分块大法万岁;

把长度为n的序列,分成sqrt(n)个块;

把查询区间按照左端点所在块的序号排个序,如果左端点所在块相同,再按右端点排序。

其实,蒟蒻我也不明白这为什么会快200多ms,但它就是会;

整个程序的复杂度为O(n * sqrt(n));

代码时间:

#include<bits/stdc++.h>
using namespace std; #define maxn 1000010
int n, m, a[maxn], cnt[maxn], ans[maxn], bein[maxn], l = 1, r, now; struct node
{
int l, r, id;
}q[maxn]; bool cmp(node a, node b)
{
return bein[a.l] == bein[b.l] ? a.r < b.r : bein[a.l] < bein[b.l];
} void add(int x)//加入操作 (右移
{
if(cnt[a[x]] == 0)
++ now;
++ cnt[a[x]];
} void del(int x)//删除(左移
{
-- cnt[a[x]];
if(cnt[a[x]] == 0)
-- now;
} void print(int x)//要从后往前输出,所以来递归输出
{
if(x / 10)
print(x / 10);
//printf("%d", x % 10);
putchar(x % 10 + '0');
//printf("K");
} int main()
{
scanf("%d", &n);//输入
for(int i = 1; i <= ceil((double) n / sqrt(n)); ++ i)
for(int j = (i - 1) * sqrt(n) + 1; j <= i * sqrt(n); ++ j)
bein[j] = i;//这是分的块
for(int i = 1; i <= n; ++ i)
scanf("%d", &a[i]);//还是输入
scanf("%d", &m);//继续输入
for(int i = 1; i <= m; ++ i)
{
scanf("%d%d", &q[i].l, &q[i].r);//还要输入
q[i].id = i;//记录下序号,cmp中要用
}
sort(q + 1, q + m + 1, cmp);//排序
/*这种作法就不需要add和del
for(int i = 1; i <= m; ++i) {
int ql = q[i].l, qr = q[i].r;
while(l < ql) now -= !--cnt[aa[l++]];
while(l > ql) now += !cnt[aa[--l]]++;
while(r < qr) now += !cnt[aa[++r]]++;
while(r > qr) now -= !--cnt[aa[r--]];
ans[q[i].id] = now;
}
*/
for(int i = 1; i <= m; ++ i)
{
while(l < q[i].l)//l右移
del(l ++);
while(l > q[i].l)//l左移
add(-- l);
while(r < q[i].r)//r右移
add(++ r);
while(r > q[i].r)//r左移
del(r --);
ans[q[i].id] = now;
}
for(int i = 1; i <= m; ++ i)
{
print(ans[i]);//输出
printf("\n");//记得换行
}
return 0;
}

因为在本蒟蒻最开始学的时候没有懂,所以有拜读了WAMonster大佬的文章,可能在思路上会有部分相同,而且我也强力推荐这位大佬的博客,写的特别好。

洛谷 题解 SP3267 【DQUERY - D-query】的更多相关文章

  1. 洛谷 题解 UVA572 【油田 Oil Deposits】

    这是我在洛谷上的第一篇题解!!!!!!!! 这个其实很简单的 我是一只卡在了结束条件这里所以一直听取WA声一片,详细解释代码里见 #include<iostream> #include&l ...

  2. 洛谷 P2056 [ZJOI2007]捉迷藏 || bzoj 1095: [ZJOI2007]Hide 捉迷藏 || 洛谷 P4115 Qtree4 || SP2666 QTREE4 - Query on a tree IV

    意识到一点:在进行点分治时,每一个点都会作为某一级重心出现,且任意一点只作为重心恰好一次.因此原树上任意一个节点都会出现在点分树上,且是恰好一次 https://www.cnblogs.com/zzq ...

  3. 洛谷 题解 P1600 【天天爱跑步】 (NOIP2016)

    必须得说,这是一道难题(尤其对于我这样普及组205分的蒟蒻) 提交结果(NOIP2016 天天爱跑步): OJ名 编号 题目 状态 分数 总时间 内存 代码 / 答案文件 提交者 提交时间 Libre ...

  4. 洛谷题解P4314CPU监控--线段树

    题目链接 https://www.luogu.org/problemnew/show/P4314 https://www.lydsy.com/JudgeOnline/problem.php?id=30 ...

  5. 洛谷/SPOJ SP3267 题解

    若想要深入学习主席树,传送门. Description: 给定数列 \(\{a_n\}\) ,求闭区间 \([l,r]\) 的互异的个数. Method: 扫描序列建立可持续化线段树,若此元素是第一次 ...

  6. 洛谷题解 CF777A 【Shell Game】

    同步题解 题目翻译(可能有童鞋没读懂题面上的翻译) 给你三张牌0,1,2. 最初选一张,然后依次进行n次交换,交换规则为:中间一张和左边的一张,中间一张和右边一张,中间一张和左边一张...... 最后 ...

  7. 洛谷题解 CF807A 【Is it rated?】

    同步题解 题目 好吧,来说说思路: 1.先读入啦~(≧▽≦)/~啦啦啦 2.判断a[i]赛前赛后是否同分数,如果分数不同,则输出,return 0 . 3.如果同分数,则判断a[i]赛前(或赛后)是否 ...

  8. 洛谷题解 P1138 【第k小整数】

    蒟蒻发题解了 说明:此题我用的方法为桶排(我翻了翻有人用了桶排只不过很难看出来,可能有些重复的,这个题只是作为一个专门的桶排来讲解吧) (不会算抄袭吧 ‘QWaWQ’) 简单来说(会的人跳过就行): ...

  9. 【洛谷题解】P2303 [SDOi2012]Longge的问题

    题目传送门:链接. 能自己推出正确的式子的感觉真的很好! 题意简述: 求\(\sum_{i=1}^{n}gcd(i,n)\).\(n\leq 2^{32}\). 题解: 我们开始化简式子: \(\su ...

随机推荐

  1. 决策树(上)-ID3、C4.5、CART

    参考资料(要是对于本文的理解不够透彻,必须将以下博客认知阅读,方可全面了解决策树): 1.https://zhuanlan.zhihu.com/p/85731206 2.https://zhuanla ...

  2. Android Debug 之 Log 最佳实践

    本文微信公众号「AndroidTraveler」首发. 背景 在开发过程中,调试是必不可少的一项工作. 当我们要确定项目的逻辑时,当我们要了解界面的生命周期时,当我们发现新写的逻辑与期望效果不一致时, ...

  3. PHP 在 Laravel 中动态隐藏 API 字段

    我最近在 Laravel Brasil 社区看到一个问题,结果比看起来更有趣.想象一下你有一个 UsersResource 用下面的实现: <?php namespace App\Http\Re ...

  4. [剑指offer]删除链表中重复的结点(把重复的都删掉,1个不留)

    ①题目 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. 例如,链表1->2->3->3->4->4->5 处理后为 ...

  5. hadoop伪分布式

    一.安装jdk 1.下载解压 2.配置环境变量 配置成功: 二.ssh免密码登录 https://www.cnblogs.com/suwy/p/9326087.html 三.hadoop伪分布式配置 ...

  6. nyoj 58-最少步数 (BFS)

    58-最少步数 内存限制:64MB 时间限制:3000ms Special Judge: No accepted:17 submit:22 题目描述: 这有一个迷宫,有0~8行和0~8列: 1,1,1 ...

  7. hdu 4337 King Arthur's Knights (Hamilton)

    King Arthur's KnightsTime Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Oth ...

  8. Redis单节点数据同步到Redis集群

    一:Redis集群环境准备 1:需要先安装好Redis集群环境并配置好集群 192.168.0.113 7001-7003 192.168.0.162 7004-7006 2:检查redis集群 [r ...

  9. 【Flume】Flume基础之安装与使用

    1.Flume简介 ​ (1) Flume提供一个分布式的,可靠的,对大数据量的日志进行高效收集.聚集.移动的服务,Flume只能在Unix环境下运行. ​ (2) Flume基于流式架构,容错性强, ...

  10. GitHub远程库的搭建以及使用

    GitHub远程库的搭建 一).配置SSH 步骤: 1).注册GitHub账号 2).本地git仓库与远程的GitHub仓库的传输要通过SSH进行加密 3).创建SSH key ​ 1.检查在用户主目 ...