洛谷 题解 SP3267 【DQUERY - D-query】
今天机房讲了莫队。
但是蒟蒻我并没有听懂,所以晚上回家恶补,才弄明白莫队。
莫队是莫涛大神发明的,它的作用就是用优秀的复杂度求解于一些区间之间的操作,莫队其实就是一个优雅的暴力,它的复杂度是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】的更多相关文章
- 洛谷 题解 UVA572 【油田 Oil Deposits】
这是我在洛谷上的第一篇题解!!!!!!!! 这个其实很简单的 我是一只卡在了结束条件这里所以一直听取WA声一片,详细解释代码里见 #include<iostream> #include&l ...
- 洛谷 P2056 [ZJOI2007]捉迷藏 || bzoj 1095: [ZJOI2007]Hide 捉迷藏 || 洛谷 P4115 Qtree4 || SP2666 QTREE4 - Query on a tree IV
意识到一点:在进行点分治时,每一个点都会作为某一级重心出现,且任意一点只作为重心恰好一次.因此原树上任意一个节点都会出现在点分树上,且是恰好一次 https://www.cnblogs.com/zzq ...
- 洛谷 题解 P1600 【天天爱跑步】 (NOIP2016)
必须得说,这是一道难题(尤其对于我这样普及组205分的蒟蒻) 提交结果(NOIP2016 天天爱跑步): OJ名 编号 题目 状态 分数 总时间 内存 代码 / 答案文件 提交者 提交时间 Libre ...
- 洛谷题解P4314CPU监控--线段树
题目链接 https://www.luogu.org/problemnew/show/P4314 https://www.lydsy.com/JudgeOnline/problem.php?id=30 ...
- 洛谷/SPOJ SP3267 题解
若想要深入学习主席树,传送门. Description: 给定数列 \(\{a_n\}\) ,求闭区间 \([l,r]\) 的互异的个数. Method: 扫描序列建立可持续化线段树,若此元素是第一次 ...
- 洛谷题解 CF777A 【Shell Game】
同步题解 题目翻译(可能有童鞋没读懂题面上的翻译) 给你三张牌0,1,2. 最初选一张,然后依次进行n次交换,交换规则为:中间一张和左边的一张,中间一张和右边一张,中间一张和左边一张...... 最后 ...
- 洛谷题解 CF807A 【Is it rated?】
同步题解 题目 好吧,来说说思路: 1.先读入啦~(≧▽≦)/~啦啦啦 2.判断a[i]赛前赛后是否同分数,如果分数不同,则输出,return 0 . 3.如果同分数,则判断a[i]赛前(或赛后)是否 ...
- 洛谷题解 P1138 【第k小整数】
蒟蒻发题解了 说明:此题我用的方法为桶排(我翻了翻有人用了桶排只不过很难看出来,可能有些重复的,这个题只是作为一个专门的桶排来讲解吧) (不会算抄袭吧 ‘QWaWQ’) 简单来说(会的人跳过就行): ...
- 【洛谷题解】P2303 [SDOi2012]Longge的问题
题目传送门:链接. 能自己推出正确的式子的感觉真的很好! 题意简述: 求\(\sum_{i=1}^{n}gcd(i,n)\).\(n\leq 2^{32}\). 题解: 我们开始化简式子: \(\su ...
随机推荐
- 决策树(上)-ID3、C4.5、CART
参考资料(要是对于本文的理解不够透彻,必须将以下博客认知阅读,方可全面了解决策树): 1.https://zhuanlan.zhihu.com/p/85731206 2.https://zhuanla ...
- Android Debug 之 Log 最佳实践
本文微信公众号「AndroidTraveler」首发. 背景 在开发过程中,调试是必不可少的一项工作. 当我们要确定项目的逻辑时,当我们要了解界面的生命周期时,当我们发现新写的逻辑与期望效果不一致时, ...
- PHP 在 Laravel 中动态隐藏 API 字段
我最近在 Laravel Brasil 社区看到一个问题,结果比看起来更有趣.想象一下你有一个 UsersResource 用下面的实现: <?php namespace App\Http\Re ...
- [剑指offer]删除链表中重复的结点(把重复的都删掉,1个不留)
①题目 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. 例如,链表1->2->3->3->4->4->5 处理后为 ...
- hadoop伪分布式
一.安装jdk 1.下载解压 2.配置环境变量 配置成功: 二.ssh免密码登录 https://www.cnblogs.com/suwy/p/9326087.html 三.hadoop伪分布式配置 ...
- nyoj 58-最少步数 (BFS)
58-最少步数 内存限制:64MB 时间限制:3000ms Special Judge: No accepted:17 submit:22 题目描述: 这有一个迷宫,有0~8行和0~8列: 1,1,1 ...
- hdu 4337 King Arthur's Knights (Hamilton)
King Arthur's KnightsTime Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Oth ...
- Redis单节点数据同步到Redis集群
一:Redis集群环境准备 1:需要先安装好Redis集群环境并配置好集群 192.168.0.113 7001-7003 192.168.0.162 7004-7006 2:检查redis集群 [r ...
- 【Flume】Flume基础之安装与使用
1.Flume简介 (1) Flume提供一个分布式的,可靠的,对大数据量的日志进行高效收集.聚集.移动的服务,Flume只能在Unix环境下运行. (2) Flume基于流式架构,容错性强, ...
- GitHub远程库的搭建以及使用
GitHub远程库的搭建 一).配置SSH 步骤: 1).注册GitHub账号 2).本地git仓库与远程的GitHub仓库的传输要通过SSH进行加密 3).创建SSH key 1.检查在用户主目 ...