约束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[256] = { 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 }; int l = -1; while (x >= 256) { l += 8; x >>= 8; } return l + log_2[x]; }
解决LCA的代码:
//drunk,fix later #include<iostream> #include<cstdio> #include<cmath> #define Re register using namespace std; const int MAXN=1000005; inline int rd() { int ret=0,f=1; char c; while(c=getchar(),!isdigit(c))f=c=='-'?-1:1; while(isdigit(c))ret=ret*10+c-'0',c=getchar(); return ret*f; } int n,m,st; struct Edge { int next,to; } e[MAXN<<1]; 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]]+1; 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][32],g[MAXN][32]; int lookUp[MAXN]; inline int computeType(int x) { int sum=0; for(Re int i=L[x]; i<=R[x]-1; i++) sum<<=1,sum+=(dep[i+1]>dep[i]); return sum; } inline void calcPos(int x) { int len=0,po=0,cnt=0,mn=1<<30,mnid; len=blockLen; for(Re int i=len; i>=0; i--) { po++; if((1<<i)&x) cnt++; else cnt--; if(cnt<mn) mn=cnt,mnid=po; } lookUp[x]=mnid-1; } void build() { blockLen=log2(tot)/2; num=tot/blockLen; if(tot%blockLen) num++; for(Re int i=1; i<=num; i++) { L[i]=(i-1)*blockLen+1; R[i]=i*blockLen; } for(Re int i=tot+1; i<=R[num]; i++) dep[i]=(1<<30); for(Re int i=1; i<=tot; i++)bl[i]=(i-1)/blockLen+1; for(Re int i=0; i*i<=tot; i++) calcPos(i); for(Re int i=1; i<=num; i++)blockTyp[i]=computeType(i); for(Re int i=1; i<=num; i++) g[i][0]=(i-1)*blockLen+lookUp[blockTyp[i]],f[i][0]=dep[g[i][0]]; //offset! for(Re int j=1; (1<<j)<=num; j++) for(Re int i=1; i<=num; i++) if(f[i][j-1]<f[i+(1<<(j-1))][j-1]) f[i][j]=f[i][j-1],g[i][j]=g[i][j-1]; else f[i][j]=f[i+(1<<(j-1))][j-1],g[i][j]=g[i+(1<<(j-1))][j-1]; } inline int inBlockQuery(int x,int y) { int u=blockTyp[bl[x]],v=(bl[x]-1)*blockLen+lookUp[u]; if(x<=v&&v<=y) return v; int sav=bl[x]; x-=L[sav]-1;y-=L[sav]-1; u>>=(blockLen-y); u&=(~((-1)<<(y-x))); return (sav-1)*blockLen+lookUp[u]-(blockLen-y); } int query(int x,int y) { if(bl[x]==bl[y]) return inBlockQuery(x,y); int mn=1<<30,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]+1,r=bl[y]-1,len; if((r-l+1>0)) len=log2(r-l+1); else return mnid; if(f[l][len]<mn) mn=f[l][len],mnid=g[l][len]; if(f[r-(1<<len)+1][len]<mn) mn=f[r-(1<<len)+1][len],mnid=g[r-(1<<len)+1][len]; return mnid; } int main() { n=rd();m=rd();st=rd(); int x,y; for(Re int i=1; i<=n-1; i++) { x=rd();y=rd(); add(x,y);add(y,x); } dfs(st,0);build(); for(int i=1; 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 0; }
本文来自博客园,作者:GhostCai,转载请注明原文链接:https://www.cnblogs.com/ghostcai/p/9280720.html