还没有去省里竞赛过~~这回怎么也得争到名额,加油~~一步步来,九月份去了~我只是菜鸟,还太多不懂,只能一个个来,前两天看了下LCA问题,和它与之紧密联系的是RMQ算法,今天无论如何得把这个弄懂了~~
RMQ算法全称为(Range Minimum/Maximum Query)意思是给你一个长度为n的数组A,求出给定区间的最值的下标。当然我们可以采用枚举,但是我们也可以使用线段树来优化,复杂度为(nlogn),但是最好的办法是采用Sparse_Table算法,简称ST算法。他能在进行(nlogn)的预处理后达到n(1)的效率。下面来分析下最大值和最小值,都要用到DP的思想。
最小值(Mininun):我们可以用F(i,j)表示区间[i,i+2^j-1]间的最小值。我们可以开辟数组来保存F(i,j)的值,例如:F(2,4)就是保存区间[2,2+2^4-1]=[2,17]的最小值。那么F(i,0)的值是确定的,就为i这个位置所指的元素值,这时我们可以把区间[i,i+2^j-1]平均分为两个区间,因为j>=1的时候该区间的长度始终为偶数,可以分为区间[i,i+2^(j-1)-1]和区间[i+2^(j-1)-1,i+2^j-1],即取两个长度为2^(j-1)的块取代和更新长度为2^j的块,那么最小值就是这两个区间的最小值的最小值,动态规划为:F[i,j]=min(F[i,j-1],F[i+2^(j-1),j-1]).同理:最大值就是F[i,j]=max(F[i,j-1],F[i+2^(j-1),j-1]).
现在求出了F[i,j]之后又是怎样求出最大值或者最小值的,怎么转换为o(1)这种算法的~这就是ST算法:
这个时候询问时只要取k=ln(j-i+1)/ln2即可,那么可以令A为i到2^k的块,和B为到2^k结束的长度为2^k的块;那么A,B都是区间[i,j]的子区间,所以即求A区间的最小值和B区间的最小值的最小值。这个时候动态规划为:RMQ(i,j)=min(F[i,k],F[j-2^k+1,k]);
下面是求区间最小值的模板:
#include
#include
#include
#include
using namespace std;
const int MAX=10010;
#define max(a,b) a>b?a:b
#define min(a,b) a
当然对于RMQ并不只有这个用法,我们可以用它来解决LCA问题。
假设LCA(T,u,v)表示在有根树T中,询问一个离根最远的结点x,使得x为u,v的公共祖先。现在分析下LCA向RMQ问题转化的过程:
对有根树T进行深度优先遍历(DFS),将遍历到的结点按照顺序记录下来,那么我们会得到一个长度为2N-1的序列,称之为T的欧拉序列F,设序列Depth是DFS遍历过程中的结点深度的变化情况。其中每一个结点都会出现在欧拉序列F中,我们记录结点u在欧拉序列中出现的第一个位置pos(u);根据DFS的特性,对于任意两个结点u,v,那么从pos(u)(也就是第一次访问u的时候)到pos(v)(第一次访问v)的过程中,所经历的路径为F(pos(u).....pos(v)),虽然这些包括u的后代,但是其深度最小的结点一定是u和v的LCA(公共祖先),不论pos(u)与pos(v)的关系如何,都一定有LCA(T,u,v)=RMQ(Depth,pos(u),pos(v));
下面这个图是有根树的欧拉序列F和深度序列B已经pos(u)的变化情况:
1 深度为0
/ \ \
2 3 4 深度为1
/ \
5 6 深度为2
那么欧拉序列F:1 2 5 2 6 2 1 3 1 4 1;深度序列Depth为: 0 1 2 1 2 1 0 1 0 1 0,pos(u)为:1 2 8 10 3 5
还是以poj 1330为例:将LCA转化为RMQ求解,见代码:
#include
#include
#include
#include
#include
using namespace std;
const int MAX=20001;//定义10001RE了n次,定义太小了,以后RE后可以适当的把数组开大点
#define CLR(arr,val) memset(arr,val,sizeof(arr))
int n,total,Depth[2*MAX],pos[MAX],F[2*MAX],Indegree[MAX];
vector Adj[MAX];
bool visited[MAX];
void Init()
{ total=0;
CLR(pos,0);
CLR(Indegree,0);
CLR(visited,false);
for(int i=0;i::size_type i=0;i
例题2:NYOJ 119(士兵杀敌),一看就知道是RMQ算法,但是以前不会做,随便粘贴了别人的代码,过了,今天才自己做下,人总是再进步,谁都有过粘贴别人的代码过题的时候,但是你也在进步,重要的是坚持,RLE了n次,少在rmq()中加了if语句。
#include
#include
#include
#include
using namespace std;
const int MAX=100010;
#define max(a,b) a>b?a:b
#define min(a,b) a