LCA的在线算法, DFS编号 + ST来rmq。
2016-05-13 23:42
316 查看
先讲讲 rmq的dp思想:
加入A[i] 是我们要求区间最值的数列,F[i,j]表示从第i个数起连续2^j 个数中的最大值。
例如: A : 3 2 4 5 6 8 1 2 9 7
F[1,0] 表示从第一个数开始长度为2^0=1 的最大值, 就是三这个数。同理F[1,1]=max(3,2)=3;
F[1,2]=max(3,2,4,5)=5………..且F[i,0]=i;
初值已经有了, 求F[i,j] 长度 肯定是偶数, 所以最大值用dp 来想就是分为两段,就是两段中的max就是结果,于是我们可以很巧妙地得到状态转移方程: F[i , j] = max(F[i, j-1] , F[i+ 2^(j-1) , j-1]) ;
代码:
这里我们可以注意到:
第j 层的状态 需要 j-1 层的状态来转移。
所以 这个 j的for循环 必然要放到最外面。
至于查询 rmq(i , j)
我们知道长度为 j-i+1 , 所以 我们可以取 k= log2 (j - i + 1) ,则RMQ(A , i ,j)=max(F[I,K],F[j-2^k +1,k ] ) ;
在LCA上面:
我们假设无线图是树,我们找一个根 ,开始dfs,
用 F 记录每一个节点。
用rmq记录 每一个节点的深度。
用first记录每一个节点在F中第一次出现的坐标。
先dfs 得到 rmq,F
然后我们对F 序列进行 rmq 的dp操作,因为之前记录 这个dp存在F中的下标即可。
然后我们直接查询就好:
模板 :
加入A[i] 是我们要求区间最值的数列,F[i,j]表示从第i个数起连续2^j 个数中的最大值。
例如: A : 3 2 4 5 6 8 1 2 9 7
F[1,0] 表示从第一个数开始长度为2^0=1 的最大值, 就是三这个数。同理F[1,1]=max(3,2)=3;
F[1,2]=max(3,2,4,5)=5………..且F[i,0]=i;
初值已经有了, 求F[i,j] 长度 肯定是偶数, 所以最大值用dp 来想就是分为两段,就是两段中的max就是结果,于是我们可以很巧妙地得到状态转移方程: F[i , j] = max(F[i, j-1] , F[i+ 2^(j-1) , j-1]) ;
代码:
void RMQ(int num){ for(int j=1; j<20 ;j++) for(int i=1;i<=num;i++) if(i+ (1<<j)-1 <= num){ maxsum[i][j]= max(maxsum[i][j-1] , maxsum[i+ (1<<(j-1)) ][j-1]); minsum[i][j]= min(minsum[i][j-1] , minsum[i+ (1<<(j-1)) ][j-1]); } }
这里我们可以注意到:
第j 层的状态 需要 j-1 层的状态来转移。
所以 这个 j的for循环 必然要放到最外面。
至于查询 rmq(i , j)
我们知道长度为 j-i+1 , 所以 我们可以取 k= log2 (j - i + 1) ,则RMQ(A , i ,j)=max(F[I,K],F[j-2^k +1,k ] ) ;
在LCA上面:
我们假设无线图是树,我们找一个根 ,开始dfs,
用 F 记录每一个节点。
用rmq记录 每一个节点的深度。
用first记录每一个节点在F中第一次出现的坐标。
先dfs 得到 rmq,F
然后我们对F 序列进行 rmq 的dp操作,因为之前记录 这个dp存在F中的下标即可。
然后我们直接查询就好:
模板 :
/* * LCA 在线算法:dfs +ST(RMQ) * poj 1330 裸题 */ #include <cstdio> #include <cmath> #include <cstring> #include <ctime> #include <iostream> #include <algorithm> #include <set> #include <vector> #include <sstream> #include <queue> #include <typeinfo> #include <fstream> #include <map> #include <stack> typedef long long ll; using namespace std; const int MAXN=10010; int rmq[2*MAXN]; //rmq数组, 记录每个节点在树中的深度 struct ST{ int mm[2*MAXN]; int dp[2*MAXN][20]; //dp 直接存下标 void init(int n){ mm[0]=-1; for(int i=1;i<=n;i++){ mm[i]=((i&(i-1))==0)? mm[i-1]+1 :mm[i-1]; //???莫名其妙定义长度 dp[i][0]=i; //初始化dp数组 } for(int j=1;j<=mm ;j++) //可以看出来mm 就是一个长度为n的序列j的最大值 for(int i=1;i+(1<<j)-1 <=n ; i++){ if(rmq[dp[i][j-1]] < rmq[dp[i+(1<<(j-1))][j-1] ]){ dp[i][j]=dp[i][j-1]; } else dp[i][j]=dp[i+(1<<(j-1))][j-1]; } } int query(int a,int b){ //这个询问返回的是位置pos, F[pos]才是值 if(a>b) swap(a,b); int k=mm[b-a+1]; if(rmq[dp[a][k]] <= rmq[dp[b-(1<<k)+1][k]] ){ return dp[a][k]; } else return dp[b-(1<<k)+1][k]; } }; struct Edge{ int to,next; int w; // 用w来记录权值:即到根部的距离。 }; Edge edge[MAXN*2]; int tot,head[MAXN]; int F[MAXN*2]; // 按顺序存储节点 ,下标从1开始,长度为2*n-1 int first[MAXN]; // i在F中第一次出现的位置 int cnt; //int dir ; //保存每个点到树根的距离,很多问题中树边都有权值,会询问两点间的距离, //如果树边没权值,相当于权值为1 ST st; void init(){ tot=0; memset(head,-1,sizeof(head)); } void addedge(int u,int v){ //无向边自然加两次 edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++; } void dfs(int u,int pre,int dep){ //u起点,dep表示深度 F[++cnt] = u; // cnt就是这个节点的下标 rmq[cnt]=dep; //rmq记录节点在树中的深度 first[u]=cnt; //这个first 不会被更新掉,因为我们把continue了回去的路 for(int i=head[u];i!=-1; i=edge[i].next){ int v=edge[i].to; if(v==pre) continue; // dir[v]=dir[u] + edge[i].w; dfs(v,u,dep+1); F[++cnt] = u; //虽然continue回去的路,但是询问叶子节点还要返回:1-2-1 --.... rmq[cnt] = dep; } } void LCA_init(int root ,int node_num){ //查询LCA前的初始化 cnt=0; dfs(root,root,0); st.init(2*node_num-1); // 注意 ,这里dp的不是n,而是对F进行dp } int query_lca(int u,int v){ //查询lca(u,v) return F[st.query(first[u],first[v])]; } bool flag[MAXN]; int main(){ //freopen("1.txt","r",stdin); int T; int N; int u,v; scanf("%d",&T); while(T--){ scanf("%d",&N); init(); memset(flag,false,sizeof(flag)); for(int i=1;i<N;i++){ scanf("%d %d",&u,&v); addedge(u,v); addedge(v,u); flag[v]=true; } int root; for(int i=1;i<=N;i++) if(!flag[i]){ //某个根 root=i; break; } LCA_init(root,N); scanf("%d %d",&u,&v); printf("%d\n",query_lca(u,v)); } return 0; }
相关文章推荐
- parted 如何对大于2T的磁盘进行分区
- 电路功耗分析
- webservice之WSDL
- HttpURLConnection实现用户登录
- 《啊哈算法》 第六章 最短路径
- 完全背包
- android Popupwindow被弹出软键盘挡住
- CF Round #350 (Div. 2)D1. Magic Powder - 1(未完成)
- 谈谈经历公司那些事
- UML时序图
- 3.css控制div的显示
- PDO数据库抽象层
- BZOJ_1003_[ZJOI2006]_物流运输_(动态规划+最短路)
- Redis学习笔记(一)搭建环境
- 寻找jar包非常实用的网站
- LeetCode 16 3Sum Closest
- Linux动态查看网络流量iptraf
- 第4章 创建型模式—工厂方法模式(1)
- GitHub/GitLab创建ssh链接
- 205. Isomorphic Strings