rmq 问题一种 O(n) 预处理,O(1) 回答询问的方法
引入
现在市面上大多数解决 问题的一般是
- 预处理, 回答的线段树,平衡树。
- 预处理, 回答的 表,猫树。
- 预处理, 回答的分块加 表,这种神仙算法。
这里考虑使用 的 预处理, 回答的算法,这里用求区间最大举例。
Cartesian-tree
对于笛卡尔树,我们可以根据性质,对于一个节点 ,它的子树的任意权值都比 要小,因为笛卡尔树是满足 的性质的,所以节点 一定是管辖的一个连续的区间的。根据笛卡尔树的性质,那么 的最大值就是节点 的 的权值。
tarjan
关于 求 。这里是一种离线算法,总的复杂度为 不考虑并查集的常数。这个的讲解网上很全,我这里抛一个链接。如何用 tarjan 求 LCA 。因此我们就得到了 预处理, 回答询问的方法 。其实如果不会 也可以用其它的方法做到 (例如倍增求 )或者其它复杂度的算法。
代码
#include<bits/stdc++.h> using namespace std; struct Query{int id,pos;}; const int N = 2e6 + 1000, M = 1e5 + 1000; inline int read() { int x=0,f=1;char ch=getchar(); while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();} while (isdigit(ch)){x=x*10+ch-48;ch=getchar();} return x*f; } vector<Query> q[M]; vector<int> G[M]; int fa[M],vis[M],n,m,root,ans[N],top,val[M],rc[M],lc[M],st[M]; int find(int x) {return fa[x] == x?x:fa[x]=find(fa[x]);} void dfs(int x,int Fa) { for(auto y:G[x]) { if(y == Fa) continue;dfs(y,x);fa[y] = x; } vis[x] = 1; for(auto Q:q[x]) { if(vis[Q.pos]) ans[Q.id] = find(Q.pos); } } int main() { n = read();m = read(); for(int i = 1;i <= n;i++) val[i] = read(); for(int i = 1;i <= n;i++) { int k = top; while(k && val[i] >= val[st[k]]) k--; if(k) rc[st[k]] = i; if(k < top) lc[i] = st[k + 1]; st[++k] = i;top = k; } root = st[1]; for(int i = 1;i <= n;i++) { int a = lc[i],b = rc[i]; if(a) G[i].push_back(a); if(b) G[i].push_back(b); } for(int i = 1;i <= m;i++) { int a = read(),b = read(); q[a].push_back((Query){i,b}); q[b].push_back((Query){i,a}); } for(int i = 1;i <= n;i++) fa[i] = i; dfs(root,0); for(int i = 1;i <= m;i++) printf("%d\n", val[ans[i]]); return 0; }