【BZOJ2724】蒲公英 题解(分块+区间众数)
题目大意:给定一段长度为$n$的序列和$m$次询问,每次询问区间$[l,r]$内的最小的众数。$n\leq 40000,a_i\leq 10^9$
-----------------------------
因为$a_i\leq 10^9$,显然不能开那么大的数组。所以要离散化。对于离散化后的数组,我们维护两个值$sum[i][j]$和$p[i][j]$。$sum[i][j]$表示前$i$个块中$j$出现的次数,这个$O(n \sqrt n)$暴力枚举就好。$p[i][j]$表示块$i$到$j$的众数,这个只需开一个桶维护,然后$O(\sqrt n \sqrt n \sqrt n)$暴力枚举就好。
对于查询,我们仍然暴力把区间$[l,r]$中边边角角的部分暴力求出来,对于整块我们用之前维护的$sum$和$p$即可。
其实就是一个大模拟……细节有点小多。不会真的有人连敲代码都不会吧
代码(有些细节的地方有注释):
#include<bits/stdc++.h>
using namespace std;
const int maxn=;
int n,m,block,tot,sum[][maxn];
int last,pre[maxn],tmpnum[maxn],bucket[maxn],vis[maxn];
struct Node
{
int id,val,se;//id编号,val值,se离散化后的值
}a[];
struct node
{
int num,s;
}p[][];
inline int read()
{
int x=,f=;char ch=getchar();
while(!isdigit(ch)){if (ch=='-') f=-;ch=getchar();}
while(isdigit(ch)){x=x*+ch-'';ch=getchar();}
return x*f;
}
bool cmp1(Node a,Node b){return a.val<b.val;}//离散化排序
bool cmp2(Node a,Node b){return a.id<b.id;}
int getpos(int x)//不用维护每个区间的左右端点了,一个函数搞定
{
int pos=x/block;
if (x%block) pos++;
return pos;
}
inline void build()
{
for (int i=;i<=tot;i++)
{
memset(bucket,,sizeof(bucket));node tmp;//注意清空桶
tmp.s=tmp.num=;
for (int j=i;j<=tot;j++)
{
for (int k=(j-)*block+;k<=min(n,j*block);k++)//预处理
{
bucket[a[k].se]++;
if (bucket[a[k].se]>tmp.s)
{
tmp.s=bucket[a[k].se];
tmp.num=a[k].se;
}
else if (bucket[a[k].se]==tmp.s) tmp.num=min(tmp.num,a[k].se);
}
p[i][j]=tmp;
}
}
for (int i=;i<=tot;i++){//预处理
for (int j=;j<=n;j++) sum[i][a[j].se]=sum[i-][a[j].se];
for (int j=(i-)*block+;j<=min(n,i*block);j++) sum[i][a[j].se]++;
}
}
inline void query(int l,int r)
{
int posl=getpos(l),posr=getpos(r);
if (posr-posl<=)//如果区间范围较小直接暴力枚举即可
{
int ans=;
for (int i=l;i<=r;i++) tmpnum[a[i].se]=;//开一个桶,注意清空
for (int i=l;i<=r;i++){
tmpnum[a[i].se]++;
if (tmpnum[a[i].se]>tmpnum[ans]) ans=a[i].se;
else if (tmpnum[a[i].se]==tmpnum[ans]) ans=min(ans,a[i].se);
}
printf("%d\n",last=pre[ans]);
return;
}
int ans=p[posl+][posr-].num,maxsum=,maxnum;//用预处理的p数组维护ans
vis[ans]=;tmpnum[ans]=;
////////////暴力把边边角角统计出来//////////////
for (int i=l;i<=min(n,posl*block);i++) tmpnum[a[i].se]=,vis[a[i].se]=;
for (int i=(posr-)*block+;i<=r;i++) tmpnum[a[i].se]=,vis[a[i].se]=;
for (int i=l;i<=min(n,posl*block);i++) tmpnum[a[i].se]++;
for (int i=(posr-)*block+;i<=r;i++) tmpnum[a[i].se]++;
for (int i=l;i<=min(n,posl*block);i++){
if (vis[a[i].se]) continue;
vis[a[i].se]=;
int summ=tmpnum[a[i].se]+sum[posr-][a[i].se]-sum[posl][a[i].se];//个数
if (summ>maxsum) maxsum=summ,maxnum=a[i].se;
else if (maxsum==summ) maxnum=min(maxnum,a[i].se);
}
for (int i=(posr-)*block+;i<=r;i++){
if (vis[a[i].se]) continue;
vis[a[i].se]=;
int summ=tmpnum[a[i].se]+sum[posr-][a[i].se]-sum[posl][a[i].se];//个数
if (summ>maxsum) maxsum=summ,maxnum=a[i].se;
else if (maxsum==summ) maxnum=min(maxnum,a[i].se);
}
///////////////////////////////////////////////
if (maxsum>tmpnum[ans]+p[posl+][posr-].s) ans=maxnum;
else if (maxsum==tmpnum[ans]+p[posl+][posr-].s) ans=min(ans,maxnum);
printf("%d\n",last=pre[ans]);
}
int main()
{
n=read(),m=read();block=sqrt(n);
tot=(n+block-)/block;
for (int i=;i<=n;i++) a[i].val=read(),a[i].id=i;
sort(a+,a+n+,cmp1);a[].val=-;
for (int i=;i<=n;i++)//离散化
{
a[i].se=a[i-].se;
if (a[i].val!=a[i-].val) a[i].se++;
pre[a[i].se]=a[i].val;
}
sort(a+,a+n+,cmp2);
build();
for (int i=;i<=m;i++)
{
int l=read(),r=read();
l=(l+last-)%n+;
r=(r+last-)%n+;
if (l>r) swap(l,r);
query(l,r);
}
return ;
}
【BZOJ2724】蒲公英 题解(分块+区间众数)的更多相关文章
- 蒲公英(bzoj2724)(分块+区间众数)
Input Output Sample Input 6 3 1 2 3 2 1 2 1 5 3 6 1 5 Sample Output 1 2 1 HINT \(n <= 40000\),$ m ...
- BZOJ2724 蒲公英 【分块】
BZOJ2724 蒲公英 题目背景 亲爱的哥哥: 你在那个城市里面过得好吗? 我在家里面最近很开心呢.昨天晚上奶奶给我讲了那个叫「绝望」的大坏蛋的故事的说!它把人们的房子和田地搞坏,还有好多小朋友也被 ...
- bzoj2724: [Violet 6]蒲公英 分块 区间众数 论algorithm与vector的正确打开方式
这个,要处理各个数的话得先离散,我用的桶. 我们先把每个块里的和每个块区间的众数找出来,那么在查询的时候,可能成为[l,r]区间的众数的数只有中间区间的众数和两边的数. 证明:若不是这里的数连区间的众 ...
- BZOJ 2724: [Violet 6]蒲公英 [分块 区间众数]
传送门 题面太美不忍不放 分块分块 这种题的一个特点是只有查询,通常需要预处理:加入修改的话需要暴力重构预处理 预处理$f[i][j]$为第i块到第j块的众数,显然$f[i][j]=max{f[i][ ...
- LOJ6285 数列分块入门9(分块 区间众数)题解
题意:给出区间内的最小众数 思路:分块,离散化每个数,开vector记录每个数p出现的位置,这样就能二分出L,R以内p的个数了.众数有一个性质,用mode(a)表示集合a的众数,那么mode(a∪b) ...
- 洛谷P4168 蒲公英 分块处理区间众数模板
题面. 许久以前我还不怎么去机房的时候,一位大佬好像一直在做这道题,他称这道题目为"大分块". 其实这道题目的思想不只可以用于处理区间众数,还可以处理很多区间数值相关问题. 让我们 ...
- 【BZOJ2724】蒲公英(分块)
[BZOJ2724]蒲公英(分块) 题面 洛谷 谴责权限题的行为 题解 分块什么的都不会,根本就没写过几次. 复杂度根本不会分析,吓得我赶快来练练. 这题要求的是区间众数,显然没有什么很好的主席树之类 ...
- BZOJ2724 [Violet]蒲公英(分块)
区间众数.分块,预处理任意两块间所有数的众数,和每块中所有数的出现次数的前缀和.查询时对不是整块的部分暴力,显然只有这里出现的数可能更新答案.于是可以优美地做到O(n√n). #include< ...
- 【BZOJ 2724】 2724: [Violet 6]蒲公英 (区间众数不带修改版本)
2724: [Violet 6]蒲公英 Time Limit: 40 Sec Memory Limit: 512 MBSubmit: 1908 Solved: 678 Description In ...
随机推荐
- Kafka入门(1):概述
摘要 在本文中,我将从为什么需要消息队列开始讲起,举两个小例子,跟你聊聊目前消息队列的一些使用场景. 比如消息队列在复杂系统中的解耦,又比如消息队列在高并发下的场景如果让流量变得更平缓. 随后我会跟你 ...
- 最新Spark入门篇
一.Spark简介 1.什么是Spark Apache Spark是一种快速的集群计算技术,基于Hadoop MapReduce技术,扩展了MapReduce模型,主要特性是在内存中集群计算,速度更快 ...
- 一篇夯实一个知识点系列--python生成
写在前面 本系列目的:一篇文章,不求鞭辟入里,但使得心应手. 迭代是数据处理的基石,在扫描内存无法装载的数据集时,我们需要一种惰性获取数据的能力(即一次获取一部分数据到内存).在Python中,具有这 ...
- JVM 专题十:运行时数据区(五)堆
1. 核心概述 1.1 堆概述 一个进程对应一个jvm实例,一个运行时数据区,又包含多个线程,这些线程共享了方法区和堆,每个线程包含了程序计数器.本地方法栈和虚拟机栈. 一个jvm实例只存在一个堆内存 ...
- bzoj3687简单题*
bzoj3687简单题 题意: 给个集合,求所有子集的元素和的异或和.集合元素个数≤1000,整个集合的元素和≤2000000 题解: 用bitset维护每个子集元素和的个数是奇数还是偶数.每次读入一 ...
- iis 0x80070032 Cannot read configuration file because it exceeds the maximum file size
问题:iis部署了网站,由于webconfig文件过大(251kb,默认250kb)导致网站报错 0x80070032 Cannot read configuration file because i ...
- redis linux开机启动 (简单高效)
1. 在edis下载文件包中找 redis/utils 找到redis_init_script 将它拷贝到 /etc/init.d 目录并重命名为redis cd redis cd utils mv ...
- Java面试题汇总(持续更新)
1. ==和equals的区别 答: 基础数据类型比较:只能使用==,比较值是否相等 引用数据类型比较: 没有重写equals方法:==和equals没有区别,比较的都是引用是否指向了同一块内存 重写 ...
- 【Python学习笔记二】开始学习啦!如何在IDEA中新建python文件
1.新建module 2.选择本地安装的python 3.右键新建的module,创建python file就可以开始编程了 4.有时候回出现无法识别python内建函数的问题,就是运行没 ...
- 跳过Google开机设置/验证/向导
Google 的开机设置向导,亦或称作开机验证,对于刷机党来说最熟悉不过了.一般情况下,刷类原生或是原生系统,再刷 Gapps,开机就需要进行一些 Google 验证.这些验证,与国内的手机厂商所设置 ...