How far away ?
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4244 Accepted Submission(s): 1617
参考:
这篇博客写的非常不错,我就是看这个学会的。
第一次写最近公共祖先问题,用的邻接表指针。
对于一棵有根树,就会有父亲结点,祖先结点,当然最近公共祖先就是这两个点所有的祖先结点中深度最大的一个结点。
0
|
1
/ \
2 3
比如说在这里,如果0为根的话,那么1是2和3的父亲结点,0是1的父亲结点,0和1都是2和3的公共祖先结点,但是1才是最近的公共祖先结点,或者说1是2和3的所有祖先结点中距离根结点最远的祖先结点。
在求解最近公共祖先为问题上,用到的是Tarjan的思想,从根结点开始形成一棵深搜树,非常好的处理技巧就是在回溯到结点u的时候,u的子树已经遍历,这时候才把u结点放入合并集合中,这样u结点和所有u的子树中的结点的最近公共祖先就是u了,u和还未遍历的所有u的兄弟结点及子树中的最近公共祖先就是u的父亲结点。以此类推。。这样我们在对树深度遍历的时候就很自然的将树中的结点分成若干的集合,两个集合中的所属不同集合的任意一对顶点的公共祖先都是相同的,也就是说这两个集合的最近公共最先只有一个。对于每个集合而言可以用并查集来优化,时间复杂度就大大降低了,为O(n + q),n为总结点数,q为询问结点对数。
算法名称 | 针对问题 | 时间消耗 | 空间消耗 |
ST算法 | 一般RMQ问题 | O(Nlog2N)-O(1) | O(Nlog2N) |
Tarjan算法 | LCA问题 | O(Na(N) + Q) | O(N) |
±1RMQ算法 | ±1RMQ问题 | O(N)-O(1) | O(N) |
不懂算法的可以找棵树模拟一下递归的过程。
给出Tarjan离线算法的做法:
1 //62MS 4232K 1750 B G++ 2 /* 3 4 第一题LCA: 5 在看完算法后发现还未能完全实现,因为理解还不透彻,所以想先做 6 一题然后再从题入手。看了别人的结题报告后才写的,感觉还好。 7 这一题就是模板题了。这里采用的是离线算法Tarjan。 8 9 树两点的间的最近距离=根节点分别到两点间的最近距离-2*根节点到两点的LCA距离。 10 11 ans = dis[u] + dis[v] - 2 * dis[lca(v, v)] 12 13 */14 #include15 #include 16 #include 17 #define N 4000518 using namespace std;19 struct node{20 int v;21 int d;22 node(int a,int b){23 v=a;d=b;24 }25 };26 vector child[N],V[N]; //分别记录树和询问的邻接表 27 int set[N]; 28 int vis[N];29 int dis[N];30 int res[205][3]; //res[i][0]=u;res[i][1]=v;res[i][2]=LCA(u,v); 31 int n;32 void init()33 {34 for(int i=0;i<=n;i++){ 35 child[i].clear();36 V[i].clear();37 }38 memset(vis,0,sizeof(vis));39 memset(dis,0,sizeof(dis));40 memset(res,0,sizeof(res));41 }42 int find(int x)43 {44 if(set[x]!=x) set[x]=find(set[x]);45 return set[x];46 }47 void merge(int x,int y)48 {49 int x0=find(x);50 int y0=find(y);51 set[y0]=x0;52 } 53 void LCA(int u) //tarjan算法 54 {55 set[u]=u;56 vis[u]=true;57 for(int i=0;i
再给出基于RMQ在线算法的做法:
1 //78MS 13100K 1892 B G++ 2 #include3 #include 4 #include 5 #define N 40005 6 struct node{ 7 int u,v,d; 8 int next; 9 }edge[2*N]; //邻接表 10 int tot,head[N]; //记录先序遍历出现次序,邻接表头 11 int set[2*N],first[N]; //记录父节点与次序数 12 int dep[2*N],dis[N]; //记录深度和距离 13 int dp[2*N][30];14 bool vis[N];15 void addedge(int u,int v,int d,int &k) //加边 16 {17 edge[k].u=u;18 edge[k].v=v;19 edge[k].d=d;20 edge[k].next=head[u];21 head[u]=k++;22 }23 int Min(int a,int b) //比较的是深度 24 {25 return dep[a] y){61 int temp=x;x=y;y=temp;62 }63 return set[RMQ(x,y)];64 }65 int main(void)66 {67 int t,n,q;68 scanf("%d",&t);69 while(t--)70 {71 int u,v,d;72 int num=0;73 scanf("%d%d",&n,&q);74 memset(head,-1,sizeof(head));75 memset(vis,false,sizeof(vis));76 for(int i=1;i