P4168 [Violet] 蒲公英题解
洛谷题目链接:[Violet] 蒲公英
一道分块好题,调了整整一上午
一句话题意:在线求区间众数
考虑到众数没有可加性,所以一般数据结构是不好维护的,这个时候就要用分块了,分块可以维护一些数据结构无法维护的复杂神秘数据,对于区间众数就可以用分块维护
考虑暴力做法,对于每一个询问,暴力扫描,时间复杂度\(O(n^2)\),显然无法AC此题,故考虑分块
将区间分为T块,对于每个询问,有以下情况
- 查询区间大小小于块长
- 查询区间大小大于块长
对于第一种情况,我们直接暴力扫描区间\((l ,r)\),时间复杂度\(O(T)\);
对于第二种情况,常见的分块思想是将区间内整块预处理,然后暴力扫描左右的散块,所以,我们要预处理对于每个区间,从整块i,到整块j的众数\(p(i,j)\),预处理直接暴力枚举,时间复杂度\(O(nT^2)\)。思考第二种情况的答案可能情况,发现只能来自整块的众数或者散块中的数,感觉是废话,注意这里散块中的数不一定是散块的众数,所以每一个数都需要遍历(笔者就是这里错了,调了半天),这里,我们因为要遍历每一个数出现次数,如果暴力跑与朴素算法无异,所以需要前缀和优化,对于\(s[i][j]\),表示从第1块到第i块,j出现的次数,可以\(O(1)\)求出整块某个数出现次数。
对于最终答案,我们得出:
- 通过p数组得出只在整块出现的众数。
- 暴力统计散块中数出现的次数,加上整块中这个数出现次数,得出众数。
- 比较两个答案,得出最终结果
最后,我们思考时间复杂度如何最优。时间瓶颈来自于\(O(nT^2)\)预处理,根据基本不等式,当\(T=\sqrt[3]{n}\)时时间复杂度最优,为\(O(n^{\frac{5}{3}})\),实际测试下来,\(T=\sqrt{n}\)也可以过,本题解给出代码为\(T=\sqrt{n}\),这和暴力有什么区别。
等等,还有什么没有考虑?再看一眼数据范围,\(a\le1e9\),要离散化。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=5e4+10,M=500;
int s[M][N];
int p[M][M];
int a[N];
int b[N],num;
int c[N];
int d[N];
int n,m;
int T,cnt;
int cntnum[N];
struct node
{
int l,r;
} pos[M];
void dis()
{
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)
{
if(i==1 || a[i]!=a[i-1])
b[++num]=a[i];
}
}
int query(int x)
{
return lower_bound(b+1,b+num+1,x)-b;
}
void init()
{
for(int i=1;i<=n;i++) c[i]=query(d[i]);//c[i]表示下标为i的原数的离散值
T=pow(n,1/2.0);
int l=1,r=T;
while(r<=n)
{
pos[++cnt].l=l;
pos[cnt].r=r;
l=r+1;
r=l+T-1;
}
if(l<=n)
{
pos[++cnt].l=l;
pos[cnt].r=n;
}//分块
for(int i=1;i<=cnt;i++)
{
int l=pos[i].l,r=pos[i].r;
for(int j=1;j<=num;j++)
{
int res=0;
for(int i=l;i<=r;i++)
{
if(j==c[i]) res++;
}
s[i][j]=s[i-1][j]+res;
}
}// 预处理s数组,为前缀和数组
int mx=0;
for(int i=1;i<=cnt;i++)
{
for(int j=i;j<=cnt;j++)
{
int l=pos[i].l,r=pos[j].r;
memset(cntnum,0,sizeof(cntnum)),mx=0;
for(int k=l;k<=r;k++)
{
int x=c[k];
cntnum[x]++;
if(cntnum[x]>mx || (cntnum[x]==mx && x<p[i][j])) mx=cntnum[x],p[i][j]=x;
}
}
}//预处理p数组,注意,p存的是离散化后的值
}
pair<int,int> check(int pl,int pr,int l0,int r0)
{
int r=pos[pl].l;
int mx=0,res=0;//mx为众数个数,res为众数
memset(cntnum,0,sizeof(cntnum));
for(int i=l0;i<r;i++)
{
int x=c[i];
int y=s[pr][x]-s[pl-1][x];
cntnum[x]++;
if(cntnum[x]+y>mx || (cntnum[x]+y==mx && x<res)) mx=cntnum[x]+y,res=x;
}//左侧散块处理
int l=pos[pr].r;
for(int i=l+1;i<=r0;i++)
{
int x=c[i];
int y=s[pr][x]-s[pl-1][x];
cntnum[x]++;
if(cntnum[x]+y>mx || (cntnum[x]+y==mx && x<res)) mx=cntnum[x]+y,res=x;
}//右侧散块处理
return {res,mx};
}
void solve()
{
int ans=0;
for(int i=1;i<=m;i++)
{
int l0,r0;
scanf("%d%d",&l0,&r0);
l0=(l0+ans-1)%n+1;
r0=(r0+ans-1)%n+1;
if(r0<l0) swap(r0,l0);
ans=0;
if(r0-l0+1<T)
{
int mx=0;
memset(cntnum,0,sizeof(cntnum));
for(int i=l0;i<=r0;i++)
{
int x=c[i];
cntnum[x]++;
if(cntnum[x]>mx || (cntnum[x]==mx && x<ans)) mx=cntnum[x],ans=x;
}
ans=b[ans];
}
else
{
int pl=ceil(l0/(double)T),pr=ceil(r0/(double)T);
if(l0!=pos[pl].l) pl++;//最左侧整块
if(r0!=pos[pr].r) pr--;//最右侧整块
int now=p[pl][pr];
pair <int,int> mode;
mode=check(pl,pr,l0,r0);
int sca=mode.second;//散块众数数量
int cop=s[pr][now]-s[pl-1][now];//整块的众数数量
if(cop>sca) ans=b[now];
else
{
if(sca>cop) ans=b[mode.first];
else ans=min(b[now],b[mode.first]);
}
}
printf("%d\n",ans);
}
}
int main(){
// freopen("P4168_1.in","r",stdin);
// freopen("ans.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),d[i]=a[i];
dis();
init();
solve();
}
码风冗长,请谅解
P4168 [Violet] 蒲公英题解的更多相关文章
- 洛谷 P4168 [Violet]蒲公英 解题报告
P4168 [Violet]蒲公英 题目背景 亲爱的哥哥: 你在那个城市里面过得好吗? 我在家里面最近很开心呢.昨天晚上奶奶给我讲了那个叫「绝望」的大坏蛋的故事的说!它把人们的房子和田地搞坏,还有好多 ...
- P4168 [Violet]蒲公英 区间众数
$ \color{#0066ff}{ 题目描述 }$ 在乡下的小路旁种着许多蒲公英,而我们的问题正是与这些蒲公英有关. 为了简化起见,我们把所有的蒲公英看成一个长度为n的序列 \((a_1,a_2.. ...
- [洛谷P4168][Violet]蒲公英
题目大意:有$n(n\leqslant4\times10^4)$个数,$m(m\leqslant5\times10^4)$个询问,每次问区间$[l,r]$内的众数,若相同输出最小的,强制在线. 题解: ...
- 洛谷 P4168 [Violet] 蒲公英
历尽千辛万苦终于AC了这道题目... 我们考虑1个区间\([l,r]\), 被其完整包含的块的区间为\([L,R]\) 那么众数的来源? 1.\([l,L)\)或\((R,r]\)中出现的数字 2.\ ...
- P4168 [Violet]蒲公英
神仙分块题?其实还是很简单的,res[i][j]表示第i块到第j块的众数,然后再用sum[i][j]表示前i块中j这个种类出现的次数,然后分块瞎搞就行了,感觉我写的十分简洁,好评( //author ...
- p4168 [Violet]蒲公英(分块)
区间众数的重题 和数列分块入门9双倍经验还是挺好的 然后开O2水过 好像有不带log的写法啊 之后在补就是咕咕咕 // luogu-judger-enable-o2 #include <cstd ...
- luogu P4168 [Violet]蒲公英
嘟嘟嘟 分块经典题竟然是一道黑题…… 分块求区间众数的大体思想是对于询问区间[L, R],预处理出这中间的整块的众数,然后统计两边零散的数在[L, R]中出现的次数,最后取出现次数最多且最小的数. 因 ...
- Luogu P4168 [Violet]蒲公英 分块
这道题算是好好写了.写了三种方法. 有一个好像是$qwq$$N\sqrt(N)$的方法,,但是恳请大佬们帮我看看为什么这么慢$qwq$(后面的第三种) 注:$pos[i]$表示$i$属于第$pos[i ...
- LG P4168 [Violet]蒲公英
\(\text{Problem}\) 强制在线静态询问区间众数 \(\text{Solution}\) 不得不说 \(vector\) 是真的慢 做 \(LOJ\) 数列分块入门 \(9\) 卡时间卡 ...
- 「分块系列」「洛谷P4168 [Violet]」蒲公英 解题报告
蒲公英 Description 我们把所有的蒲公英看成一个长度为\(n\)的序列(\(a_1,a_2,...a_n\)),其中\(a_i\)为一个正整数,表示第i棵蒲公英的种类的编号. 每次询问一个区 ...
随机推荐
- 题解:[NOIP2016 提高组] 蚯蚓
洛谷同步链接 题目传送门 前置结论 结论 对于整数 $x_1,x_2$ ,当 $x_1\geq x_2,0<p<1$ 时有: $\lfloor px_1 \rfloor \geq \lfl ...
- mockito测试final类/static方法/自己new的对象
先准备几个类,方便后面讲解: public final class FinalSampleUtils { public static String foo() { return "aaa&q ...
- 高阶篇:1.4)TRIZ创新算法
本章目的:掌握最基础的ARIZ-71的矛盾矩阵的运用 1.TRIZ概论 Triz,发明问题解决理论的拉丁文翻译的首字母缩写,由苏联发明家根里奇*阿奇舒勒于1946年开始,每年动用1500人的人力,在研 ...
- 雨林木风Win10专业版解除文件夹权限的问题
有雨林木风系统的小伙伴,在win10专业版中打开一些文件夹,却出现了没有权限的问题,那要如何解除Win10文件夹的权限呢?本文中,雨林木风小编就来分享具体的解除方法,感兴趣的朋友可以一起来看看. 在 ...
- Win10打开文件夹缓慢假死的问题
越来越多的雨林木风官网用户都使用着win10专业版系统.但是,电脑总会出现问题,比如:小伙伴在打开文件夹的时候,出现打开缓慢假死的情况,这是怎么回事?其实,排除硬件本身问题,可能是Windows De ...
- Win10纯净版软件闪退无法运行的问题
最近不少雨林木风官网用户在使用Win10电脑的时候都遇到了这么一个问题,在运行某些软件的时候,程序直接闪退无法正常的额运行 ,遇到这种问题我们具体应该怎么解决呢?下面小编就带着大家一起看看吧! Win ...
- Unity Text自动缩放文本
Unity Text 里面有个 Best Fit选项,这个当超过一行文字后就会自动缩小,不是超过整个文本框才自动缩小 使用以下组件可取代Text using System.Collections.Ge ...
- go学习笔记:go func() { ... }() 启动了一个新的协程,为什么go 后面的函数没有打印?
代码如下: func main() { go func() { fmt.Println("run goroutine in closure") }() } 原因分析: go fun ...
- Linux系列之学会使用Top命令
top命令可以实时动态地查看系统的整体运行情况,是一个综合了多方信息监测系统性能和运行信息的实用工具,TOP命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,有点像win ...
- SQL Server也能玩正则表达式?二开实现比MySQL更强大的文本处理能力
技术背景:最近在搭建数据仓库,需要通过SQL Server对一系列的激光器提取其功率数,以前使用PowerBI可能是比较好实现的,现在使用SQLServer想实现这一功能还真比较困难,如果SQL Se ...