博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
bzoj2724: [Violet 6]蒲公英(分块)
阅读量:6307 次
发布时间:2019-06-22

本文共 2317 字,大约阅读时间需要 7 分钟。

 

md调了一个晚上最后发现竟然是空间开小了……明明算出来够的……

讲真其实我以前不太瞧得起分块,觉得这种基于暴力的数据结构一点美感都没有。然而今天做了这道分块的题才发现分块的暴力之美(如果我空间没有开小就更美了)

我们先将整个数组分块,设块的大小为$T$

我们先预处理出所有以块边界为端点的区间的答案,即$ans[L][R]$代表着第$L$块到第$R$块的序列所代表的答案。这个可以$O(n*n/T)$预处理

然后我们先将所有的数给离散化,然后对每一个值都开一个vector,记录这个值在数组中出现的每一个位置。比如数组的下标为1,3,5的位置都是3,那么3的vector记录的就是{1,3,5}

这个有什么用呢?我们设查询的区间为$[l,r]$,然后在这个vector里先二分查找第一个大于等于$l$的数的位置,再二分查找第一个大于$r$的数的位置,那么两个位置一减就是这个数在这个区间中的出现次数。比如查询区间$[2,4]$,我们先找到第一个大于等于2的数3,在vector中下标为2,再找第一个大于4的数为5,下标为3,那么3-2=1就是3这个数字在这个区间中的出现次数

那么,我们设$[L,R]$为查询区间之间的整块,因为我们第一步已经预处理出了所有块与块之间的答案,那么这一段之间的众数也就可以知道。那么,只有区间$[l,L-1]$和$[R+1,r]$之间的数字有可能更新答案。那么我们就去枚举这两个区间中的所有数字,然后用上面说的方法去查询它在整个查询区间内的出现次数,然后更新答案即可

复杂度为$O(n*n/T+n*T*logn)$,设块的大小为$n/sqrt{nlogn}$ ,那么时间复杂度就是$O(nsqrt{nlogn})$

其实还有一种更快的方法是先预处理出块与块之间的答案和各个数的出现次数,然后查询只要在散块里暴力增加并更新答案,之后暴力复原即可(然而我懒并不想打)

然后基本注意点都写在注解里了

1 //minamoto 2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #define inf 0x3f3f3f3f 9 using namespace std;10 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)11 char buf[1<<21],*p1=buf,*p2=buf;12 inline int read(){13 #define num ch-'0'14 char ch;bool flag=0;int res;15 while(!isdigit(ch=getc()))16 (ch=='-')&&(flag=true);17 for(res=num;isdigit(ch=getc());res=res*10+num);18 (flag)&&(res=-res);19 #undef num20 return res;21 }22 char sr[1<<21],z[20];int C=-1,Z;23 inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}24 inline void print(int x){25 if(C>1<<20)Ot();if(x<0)sr[++C]=45,x=-x;26 while(z[++Z]=x%10+48,x/=10);27 while(sr[++C]=z[Z],--Z);sr[++C]='\n';28 }29 const int N=40005,M=1005;30 int ans[M][M],a[N],b[N],cnt[N],rt[N],vis[N];31 vector
pos[N];32 int n,m,q,lastans=0,s,l,r;33 inline int query_cnt(int x){34 //查询数的出现次数,注意l和r要开全局变量 35 return upper_bound(pos[x].begin(),pos[x].end(),r)-lower_bound(pos[x].begin(),pos[x].end(),l);36 }37 void init(){38 //暴力枚举块与块之间的答案 39 for(int i=1;i<=rt[n];++i){40 memset(cnt,0,sizeof(cnt));41 int bg=s*(i-1)+1,res=a[bg];42 for(int j=bg;j<=n;++j){43 ++cnt[a[j]];44 if(cnt[a[j]]>cnt[res]||(cnt[a[j]]==cnt[res]&&a[j]
res||(t==res&&a[i]
res||(t==res&&a[i]
res||(t==res&&a[i]
r) swap(l,r);94 print(lastans=query(l,r));95 }96 Ot();97 return 0;98 }

 

转载于:https://www.cnblogs.com/bztMinamoto/p/9607299.html

你可能感兴趣的文章
《OpenACC并行编程实战》—— 导读
查看>>
机器学习:用初等数学解读逻辑回归
查看>>
如何在 Ubuntu 中管理和使用逻辑卷管理 LVM
查看>>
Oracle原厂老兵:从负面案例看Hint的最佳使用方式
查看>>
把自己Github上的代码添加Cocoapods支持
查看>>
C语言OJ项目参考(2493)四则运算
查看>>
零基础入门深度学习(二):神经网络和反向传播算法
查看>>
find和xargs
查看>>
数据结构例程—— 交换排序之快速排序
查看>>
WKWebView代理方法解析
查看>>
IOS定位服务的应用
查看>>
[SMS&WAP]实例讲解制作OTA短信来自动配置手机WAP书签[附源码]
查看>>
IOS中图片(UIImage)拉伸技巧
查看>>
【工具】系统性能查看工具 dstat
查看>>
基于zepto或jquery的手机端弹出框成功,失败,加载特效
查看>>
php引用(&)
查看>>
Delphi 操作Flash D7~XE10都有 导入Activex控件 shockwave
查看>>
oracle 学习笔记之名词解释
查看>>
MySQL Cluster搭建与测试
查看>>
python数据分析画图体验
查看>>