BZOJ.3920.Yuuna的礼物(莫队 分块套分块 分段离散化)
题目链接
**详细题解:https://www.cnblogs.com/autsky-jadek/p/4376091.html **
**代码参考自:https://www.cnblogs.com/Sakits/p/8445534.html **
思路好理解,然而就是写了一下午+一晚上。。
\(Description\)
给定一个长为n的序列,每次查询区间中出现次数k1小的数里面的k2小的数。卡空间。
\(Solution\)
将出现次数按权值分块,这样可以实现\(O(1)\)插入,\(O(sqrt(n))\)查询第k1小的出现次数
但是还需要知道第k2小的值,可以每个块维护点的个数棵平衡树,但这样插入复杂度会变高
同样可以在每个块内每个节点再套一个权值分块,同样能够\(O(1)\)插入
但是权值分块的大小是严格值域的,即出现次数为i的值都可能出现在i块里,这样空间无法承受
于是需要分段离散化,对于块i,用出现次数\(>=i\)的\(A_x\)对其离散化。
\(∑tm_i=n\),所以空间是\(O(n)\)的;时间复杂度\(O(m*sqrt(n))\)(证明见上博客 )
//细节。。
#include <cmath>
#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
#define gc() getchar()
#define pb push_back
typedef unsigned short ushort;
const int N=40005;
ushort n,size,m,A[N],B[N],bel[N],Ans[N];
int sz1[N],sum1[N],tm[N];//都可能有负
std::vector<ushort> sum2[N]/*某块中存在某数*/,ref[N],rank[N]/*某个数在某块中的位置*/,sz2[N];
struct Ask
{
int l,r,k1,k2,id;
bool operator <(const Ask &a)const
{
return bel[l]==bel[a.l]? r<a.r : bel[l]<bel[a.l];
// return bel[l]==bel[a.l]? ((l-1)/size&1 ? r>a.r : r<a.r) : bel[l]<bel[a.l];
}
}q[N];
inline ushort read()
{
ushort now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
int Get_Pos1(int p)//找到k1所在大块、and 具体的套着的那个块
{
int x=1;
for(int i=1; i<=bel[n]; ++i)
if((p-=sz1[i])<=0) {p+=sz1[x=i]; break;}
for(int tmp=std::min((int)n,x*size-1),i=(x-1)*size; i<=tmp; ++i)
if(!(p-=(sum1[i]>0))) return i;
}
int Get_Pos2(int id,int p)
{
int x=1;
for(int i=1; i<sum2[id].size(); ++i)//不能是<bel[sum2[id].size()],不如直接<size,反正它肯定会结束
if((p-=sz2[id][i])<=0) {p+=sz2[id][x=i]; break;}
for(int tmp=std::min((int)sum2[id].size(),x*size),i=(x-1)*size; i<tmp; ++i)//注意vector的边界
if(!(p-=sum2[id][i])) return i;
}
int Query(int k1,int k2)
{
int id=Get_Pos1(k1);
return ref[id][Get_Pos2(id,k2)];
}
inline void Update(int p,int delta)//仅在tm[p]>0(不是if(tm[p]))时加!
{//修改块内的块
sum1[tm[p]]+=delta;//同理 这个也要减掉
if(sum1[tm[p]] && delta==1) ++sz1[bel[tm[p]]];
if(!sum1[tm[p]] && delta==-1) --sz1[bel[tm[p]]];
int pos=rank[p][tm[p]-1];//对于相同数多次出现已是位于不同的块,so 出现次数就不用管了
sz2[tm[p]][bel[pos]]+=delta, sum2[tm[p]][pos]+=delta;
}
inline void Modify(int p,int delta)
{
if(tm[p]>0) Update(p,-1);//要把原先的次数删掉
tm[p]+=delta;
if(tm[p]>0) Update(p,1);
}
//inline void Add(int p)//WA: 没有把之前的sz1减掉
//{
// if(tm[p]>0) Update(p,-1);//要把原先的次数删掉
// ++tm[p];
// if(++sum1[tm[p]]==1) ++sz1[bel[tm[p]]];//对于外面的块 每个出现次数只计算一次
// if(tm[p]>0) Update(p,1);
//}
//inline void Subd(int p)
//{
// if(tm[p]>0) Update(p,-1);
// if(!--sum1[tm[p]]) --sz1[bel[tm[p]]];
// if(--tm[p]>0) Update(p,1);//次数-1后的加上
//}
int main()
{
n=read(), size=sqrt(n);
bel[0]=1;//得有bel[0]给vector使
for(int i=1; i<=n; ++i) A[i]=B[i]=read(),++tm[A[i]],bel[i]=i/size+1;//全从0开始分块更方便
std::sort(B+1,B+1+n);
for(int i=1; i<=n; ++i)//Discrete
{
for(int j=1; j<=tm[B[i]]; ++j)
sum2[j].pb(0), rank[B[i]].pb(ref[j].size()), ref[j].pb(B[i]);
tm[B[i]]=0;//去重
}
for(int i=1; i<=n; ++i)
for(int j=0; j<=bel[sum2[i].size()]; ++j) sz2[i].pb(0);//<=!
m=read();
for(int i=1; i<=m; ++i)
q[i].l=read(),q[i].r=read(),q[i].k1=read(),q[i].k2=read(),q[i].id=i;
std::sort(q+1,q+1+m);
for(int l=1,r=0,i=1; i<=m; ++i)
{
while(l<q[i].l) Modify(A[l++],-1);
while(l>q[i].l) Modify(A[--l],1);
while(r<q[i].r) Modify(A[++r],1);
while(r>q[i].r) Modify(A[r--],-1);
Ans[q[i].id]=Query(q[i].k1,q[i].k2);
}
for(int i=1; i<=m; ++i) printf("%d\n",Ans[i]);
return 0;
}
BZOJ.3920.Yuuna的礼物(莫队 分块套分块 分段离散化)的更多相关文章
- bzoj 3920: Yuuna的礼物
Description 转眼就要到Karin的生日了!Yuuna她们想为她准备生日礼物!现在有许多礼物被排列成了一个一维序列,每个礼物都有一个价值.Yuuna对这个序列十分感兴趣.因此,你需要多次回答 ...
- 【BZOJ 3735】苹果树 树上莫队(树分块+离线莫队+鬼畜的压行)
2016-05-09 UPD:学习了新的DFS序列分块,然后发现这个东西是战术核导弹?反正比下面的树分块不知道要快到哪里去了 #include<cmath> #include<cst ...
- bzoj3920: Yuuna的礼物(莫队+分块套分块)
思路挺简单的,但是总感觉好难写...码力还是差劲,最后写出来也挺丑的 这题显然是个莫队题,考虑怎么转移和询问... 根据莫队修改多查询少的特点,一般用修改快查询慢的分块来维护.查第$k_1$小的出现次 ...
- (原创)BZOJ 2038 小Z的袜子(hose) 莫队入门题+分块
I - 小Z的袜子(hose) 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z ...
- Bzoj 3289: Mato的文件管理 莫队,树状数组,逆序对,离散化,分块
3289: Mato的文件管理 Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 1539 Solved: 665[Submit][Status][Di ...
- BZOJ 3289: Mato的文件管理[莫队算法 树状数组]
3289: Mato的文件管理 Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 2399 Solved: 988[Submit][Status][Di ...
- BZOJ 3236 AHOI 2013 作业 莫队算法
题目大意:给出一些数,问在一个区间中不同的数值有多少种,和在一个区间中不同的数值有多少个. 思路:因为没有改动,所以就想到了莫队算法.然后我写了5K+的曼哈顿距离最小生成树,然后果断T了.(100s的 ...
- bzoj 4540 [HNOI 2016] 序列 - 莫队算法 - Sparse-Table - 单调栈
题目传送门 传送点I 传送点II 题目大意 给定一个长度为$n$的序列.询问区间$[l, r]$的所有不同的子序列的最小值的和. 这里的子序列是连续的.两个子序列不同当且仅当它们的左端点或右端点不同. ...
- 2019.01.08 bzoj3809: Gty的二逼妹子序列(莫队+权值分块)
传送门 题意:多组询问,问区间[l,r]中权值在[a,b]间的数的种类数. 看了一眼大家应该都知道要莫队了吧. 然后很容易想到用树状数组优化修改和查询做到O(mnlogamax)O(m\sqrt nl ...
随机推荐
- C++学习8-面向对象编程基础(模板)
模板 模板是一种工具,模板可以使程序员能建立具有通用类型的函数库与类库: 模板具有两种不同的形式: 函数模板 类模板 函数模板 当一个add()函数接收两个参数,因为某种特定情况,所传入的实参数据类型 ...
- C++学习6-面向对象编程基础(运算符重载、类的派生与继承、命名空间)
运算符重载 重载的运算符是具有特殊名字的函数:它们的名字由关键字operator和其后要定义的运算符号共同组成.重载的运算符是遵循函数重载的选择原则,根据不同类型或不同参数来选择不同的重载运算符. 运 ...
- NMS和soft-nms算法
非极大值抑制算法(nms) 1. 算法原理 非极大值抑制算法(Non-maximum suppression, NMS)的本质是搜索局部极大值,抑制非极大值元素. 2. 3邻域情况下NMS的实现 3邻 ...
- Python3学习笔记23-StringIO和BytesIO
StringIO 很多时候数据读取不一定是文件,也可以在内存中 StringIO顾名思义就是在内存中读写str 要把str写入StringIO,我们需要先创建一个StringIO,然后像文件一样写入即 ...
- Python3学习笔记14-迭代与列表生成式
迭代 如果给定一个list或tuple,我们可以通过for循环来遍历这个list或tuple,这种遍历我们称为迭代(Iteration) 在Python中,迭代是通过for...in来完成的. d = ...
- 【bzoj1901】dynamic ranking(带修改主席树/树套树)
题面地址(权限题) 不用权限题的地址 首先说说怎么搞带修改主席树? 回忆一般的kth问题,我们的主席树求的是前缀和,这样我们在目标区间的左右端点的主席树差分下就能求出kth. 那么我们如何支持修改操作 ...
- Android动画分类
动画分类 View动画(补间动画).帧动画.属性动画 View动画(补间动画)包括:平移.旋转.缩放.透明度,View动画是一种渐近式动画 帧动画:图片切换动画 属性动画:通过动态改变对象的属性达到动 ...
- 并发之AQS原理(一) 原理介绍简单使用
并发之AQS原理(一) 如果说每一个同步的工具各有各的强大,那么这个强大背后是一个相同的动力,它就是AQS. AQS是什么 AQS是指java.util.concurrent.locks包里的Abst ...
- Python中的各种转义符\n\r\t
转义符 描述 \ 续行符(在行尾时) \\ 反斜杠符号 ' 单引号 " 双引号 \a 响铃 \b 退格(Backspace) \e 转义 \000 空 \n 换行 \v 纵向制表符 \t 横 ...
- unittest常用的断言方法
unittest常用的断言方法 #msg:判断不成立时需要反馈的字符串 assertEqual(self, first, second, msg=None) --判断两个参数相等:first == s ...