约束RMQ
不知道为什么网上找不到太多相关的资料,所以写一个小总结,并附有能用的代码,抛砖引玉。
约束RMQ,就是RMQ区间必须满足两项之差最大为1,采用ST表的话,这时候有O(n)建表,O(1)查询的优秀复杂度
求LCA,通过DFS把原树转化为深度序列,就等价于求区间最小值 (取到的位置)
由于DFS的性质,该序列两个数之间显然相差1,所以可以使用约束RMQ解决
先总体概括一下做法:把原序列分块,块内预处理,块间做ST表
分块大小定为L=log(n)/2,这样共分D=n/L块,对这D个数(块内最小值)做正常ST表,建表复杂度O(Dlog(D))=O((n/L)(log(n)-log(L))=O(n)
我们要保证每个步骤都是O(n)的,log(n)/2的块正好消去了ST建表时的log
但在此之前,我们得处理出块内的最小值,该怎么做呢?一个正常想法就是枚举每个数,一共是O(n)复杂度
但是,这样做虽然留下了每块的最小值以及其取到的位置,若考虑查询块的一个区间,而这个区间恰好取不到最小值,这时候只能暴力枚举,就破坏了查询O(1)了
至此我们仍没有使用其±1的特殊性质,现在考虑一下。
块内一共log(n)/2个数,由乘法原理可知,本质不同的块有U=2^(log(n)/2)=n^(1/2)个,我们不妨处理出每个这种块,复杂度Ulog(n)/2,这个函数增长是小于线性的,可以认为是O(n)
这样,处理出每个块内两元素的大小关系,就可以用01唯一表示一个块了,可以用二进制存下来,作为一个块的特征,这一步复杂度O(n)
这样有一个好处,即使查询块内一个区间,我们只需要提取这个区间对应的二进制数,就可以在预处理的数组中O(1)查询了
(怎么做呢?把这段二进制数提出来,移到最右边,由于我们规定0表示小于,1表示大于,所以会贪心地选取前面的数,查表减去偏移量就可以了)
查询时,类似分块,边角的块直接查表,中间部分ST表查询,查询是O(1)的。
至此我们完成了O(n)建表,O(1)查询的约束RMQ。
一般地,对于任何一个序列,可以在O(n)时间内建成一颗笛卡尔树,把查询该序列RMQ转化为求笛卡尔树LCA,就变成O(1)的了。
想进一步提高速度,可以使用更快的log
int MYLOG (unsigned int x) {
static const int log_2[] = {
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
};
int l = -;
while (x >= ) { l += ; x >>= ; }
return l + log_2[x];
}
解决LCA的代码:
//drunk,fix later
#include<iostream>
#include<cstdio>
#include<cmath>
#define Re register
using namespace std; const int MAXN=; inline int rd() {
int ret=,f=;
char c;
while(c=getchar(),!isdigit(c))f=c=='-'?-:;
while(isdigit(c))ret=ret*+c-'',c=getchar();
return ret*f;
} int n,m,st; struct Edge {
int next,to;
} e[MAXN<<];
int ecnt,head[MAXN];
inline void add(int x,int y) {
e[++ecnt].next = head[x];
e[ecnt].to = y;
head[x] = ecnt;
} int appear[MAXN],elm[MAXN],dep[MAXN],tot;
void dfs(int x,int pre) {
appear[x]=++tot;
elm[tot]=x;
dep[tot]=dep[appear[pre]]+;
for(int i=head[x]; i; i=e[i].next) {
int v=e[i].to;
if(v==pre) continue;
dfs(v,x);
elm[++tot]=x;
dep[tot]=dep[appear[x]];
}
} int blockLen,num,L[MAXN],R[MAXN],bl[MAXN];
int blockTyp[MAXN],f[MAXN][],g[MAXN][];
int lookUp[MAXN];
inline int computeType(int x) {
int sum=;
for(Re int i=L[x]; i<=R[x]-; i++)
sum<<=,sum+=(dep[i+]>dep[i]);
return sum;
} inline void calcPos(int x) {
int len=,po=,cnt=,mn=<<,mnid;
len=blockLen;
for(Re int i=len; i>=; i--) {
po++;
if((<<i)&x) cnt++;
else cnt--;
if(cnt<mn) mn=cnt,mnid=po;
}
lookUp[x]=mnid-;
} void build() {
blockLen=log2(tot)/;
num=tot/blockLen;
if(tot%blockLen) num++;
for(Re int i=; i<=num; i++) {
L[i]=(i-)*blockLen+;
R[i]=i*blockLen;
}
for(Re int i=tot+; i<=R[num]; i++) dep[i]=(<<);
for(Re int i=; i<=tot; i++)bl[i]=(i-)/blockLen+;
for(Re int i=; i*i<=tot; i++) calcPos(i);
for(Re int i=; i<=num; i++)blockTyp[i]=computeType(i);
for(Re int i=; i<=num; i++) g[i][]=(i-)*blockLen+lookUp[blockTyp[i]],f[i][]=dep[g[i][]]; //offset!
for(Re int j=; (<<j)<=num; j++)
for(Re int i=; i<=num; i++)
if(f[i][j-]<f[i+(<<(j-))][j-]) f[i][j]=f[i][j-],g[i][j]=g[i][j-];
else f[i][j]=f[i+(<<(j-))][j-],g[i][j]=g[i+(<<(j-))][j-];
} inline int inBlockQuery(int x,int y) {
int u=blockTyp[bl[x]],v=(bl[x]-)*blockLen+lookUp[u];
if(x<=v&&v<=y) return v;
int sav=bl[x];
x-=L[sav]-;y-=L[sav]-;
u>>=(blockLen-y);
u&=(~((-)<<(y-x)));
return (sav-)*blockLen+lookUp[u]-(blockLen-y);
} int query(int x,int y) {
if(bl[x]==bl[y]) return inBlockQuery(x,y);
int mn=<<,mnid,tmp;
tmp=inBlockQuery(x,R[bl[x]]);
if(dep[tmp]<mn) mn=dep[tmp],mnid=tmp;
tmp=inBlockQuery(L[bl[y]],y);
if(dep[tmp]<mn) mn=dep[tmp],mnid=tmp;
int l=bl[x]+,r=bl[y]-,len;
if((r-l+>)) len=log2(r-l+);
else return mnid;
if(f[l][len]<mn) mn=f[l][len],mnid=g[l][len];
if(f[r-(<<len)+][len]<mn) mn=f[r-(<<len)+][len],mnid=g[r-(<<len)+][len];
return mnid;
} int main() {
n=rd();m=rd();st=rd();
int x,y;
for(Re int i=; i<=n-; i++) {
x=rd();y=rd();
add(x,y);add(y,x);
}
dfs(st,);build();
for(int i=; i<=m; i++) {
x=rd();y=rd();
if(appear[x]>appear[y]) swap(x,y);
printf("%d\n",elm[query(appear[x],appear[y])]);
}
return ;
}
约束RMQ的更多相关文章
- 区间RMQ问题
简介 RMQ (Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j里的最小(大)值 ...
- poj 3264 区间最大最小值 RMQ问题之Sparse_Table算法
Balanced Lineup Time Limit: 5000 MS Memory Limit: 0 KB 64-bit integer IO format: %I64d , %I64u Java ...
- (沒有介紹標準算法的)RMQ問題
感謝杜哥代碼滋磁 //以下是廢話 RMQ (Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中 ...
- 『数据结构』RMQ问题
RMQ(Range Minimum/Maximum Query),即区间最值问题. 对于长度为 n 的数列 A ,回答若干查询 RMQ(A,i,j)(i,j<=n) ,返回数列 A 中下标在 i ...
- RAM区间最值
RMQ (Range Minimum/Maximum Query)问题是指:对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j里的最小(大)值,也就 ...
- 学习笔记 ST算法
[引子]RMQ (Range Minimum/Maximum Query)问题: 对于长度为n的数列A,回答若干询问RMQ(A,i,j)(i,j<=n),返回数列A中下标在i,j里的最小(大)值 ...
- [Jobdu] 题目1544:数字序列区间最小值
题目描述: 给定一个数字序列,查询任意给定区间内数字的最小值. 输入: 输入包含多组测试用例,每组测试用例的开头为一个整数n(1<=n<=100000),代表数字序列的长度.接下去一行给出 ...
- 数据结构:ST表
BZOJ1699 在经历了树套树和主席树的洗礼之后,所有的数据结构都显得格外地亲切,和自然.. ST算法能够实现O(nlogn)的预处理的情况下完成O(1)的区间最值查询 虽然这要求区间是静态的,也就 ...
- 求最近公共祖先(LCA)的各种算法
水一发题解. 我只是想存一下树剖LCA的代码...... 以洛谷上的这个模板为例:P3379 [模板]最近公共祖先(LCA) 1.朴素LCA 就像做模拟题一样,先dfs找到基本信息:每个节点的父亲.深 ...
随机推荐
- FrameLayout 的使用方法
一.FrameLayout介绍 FrameLayout帧布局是最简单的布局之一,采用帧布局的容器中无论放入多少个控件,控件默认情况下左上角都对齐到容器的左上角,如果控件一样大,同一时间只能见到最上面的 ...
- HDOJ3231醉
反正一开始就是瞎几把看题,然后题意理解了,什么飞机?只能去看题解了. 呵呵,可惜,题解看了三个小时,还是一知半解,先写了. - -菜鸡超级详细题解,强行掰弯一波,等下再问问别人吧. OK,OK开始!! ...
- cocos2d-html5基础
1 环境搭建 版本Cocos2d-html5-v2.2,tomcat7.0 配置tomcat,然后直接解压Cocos2d-html5-v2.2.zip,访问解压后的根目录的index.html就可以看 ...
- bzoj 4504: K个串【大根堆+主席树】
像超级钢琴一样把五元组放进大根堆,每次取一个出来拆开,(d,l,r,p,v)表示右端点为d,左端点区间为(l,r),最大区间和值为v左端点在p上 关于怎么快速求区间和,用可持久化线段树维护(主席树?) ...
- Python标准库 os
(掌握os模块,你需要了解Linux或类Unix系统下常用命令的操作) os.name 指示你正在使用的平台.比如对于Windows,它是'nt',而对于Linux/Unix用户,它是'posix' ...
- 写一个类继承socket时遇到的问题(TCP)
主要为题出在服务器端的accept()函数,他返回两个参数,一个套接字和一个客户端的ip和端口组成的元组. 问题就出在这个套接字这里,我们继承了socket这个类,这个套接字创建的时候是通过socke ...
- 洛谷1736(二维dp+预处理)
洛谷1387的进阶版,但很像. 1387要求是“全为1的正方形”,取dp[i][j] = min(dp[i-1][j-1], min(dp[i-1][j], dp[i][j-1]))吧?这个有“只有对 ...
- 洛谷 P2023 [AHOI2009]维护序列 || 线段树加法和乘法运算
原理倒是非常简单.设原数为x,加法的lazytag为b,乘法的lazytag为a,操作数为c,那么原式为ax+b,乘上c后(ax+b)c=(ac)*x+b*c,加上c后(ax+b)+c=ax+(b+c ...
- java基本数据类型在栈中怎么存放的?
参考地址:https://www.zhihu.com/question/24747160 问:int a = 3; 首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟 ...
- freertos之特点
主要特点:协程(co-routine):任务间的中断通信机制 支持可抢占式/协作式任务调度 .FreeRTOS-MPU 内核对象可以动态或静态分配 ...