HDU-4008 Parent and son
题目:Parent and son
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4008
题目大意:
给你一棵树,编号从1-n,接下来m个询问,每个询问两个整数x、y,问你以x 作为这棵树的根,那么y 的儿子结点中编号最小的是什么,y 的后代中结点编号最小的又是哪个。
题目思路:
我们可以先以1 作为根来构建这棵树,每个结点保存:他的后代中最小编号的值minDown[],他的儿子结点中最小编号和次小编号的值minChild[][2]。查询中有可能遇到以下三种情况:
1. x 不是 y 的后代,那么当它为根结点时,答案就是minChild[y][0]、minDown[y],这是最简单的一种情况。
2. x 是 y 的后代,这种情况又可以分两种:
(1). y 不等于 1 ,那么 x 为根时,y 的儿子中编号最小的就是 y 的父亲结点和{minChild[y][0]或minChild[y][1]}比较,选小的,不直接用minChild[y][0]的原因是有可能这个孩子是 x 的祖先,这样等 x 变为根时,这个结点就成了 y 的父亲。第二问:此时 y 后代中编号最小的肯定是 1 ,因为 1 此时是 y 的后代且 1 肯定最小。
(2). y 等于 1,那么 x 为根时,y 的儿子中编号最小的也是minChild[y][0]和minChild[y][1]中选一个,第二问:后代编号最小的,可以预处理一下,遍历y 的所有儿子p,比较minDown[p]和p的大小,选小的,也像minChild[][]一样保存最小和次小,注意此时要保存次小和最小的p值,等会有用。
现在思路就清晰了,难点就在于黄色部分:怎么判断minChild[y][0]是不是x的祖先,要不要淘汰他!
解决方法:当我们利用dfs 构建以 1 作为根的树时,可以做一个特殊处理,类似线段树,确定每一个结点的左边界和右边界,现在如果a 的左右边界包含在b 的左右边界里面,那么可以认为b 是a 的祖先。PS:这点很常见,但我还真没想到。。。
细节自己小心。。。贴代码了
1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4 #define Max 100010 5 struct ENode 6 { 7 int ad; 8 int next; 9 }; 10 11 ENode en[2*Max]; 12 int eno; 13 14 int head[Max]; 15 16 void Add(int a,int b) 17 { 18 en[eno].ad=b; 19 en[eno].next=head[a]; 20 head[a]=eno; 21 eno++; 22 23 en[eno].ad=a; 24 en[eno].next=head[b]; 25 head[b]=eno; 26 eno++; 27 } 28 29 int minChild[Max][2]; 30 int minDown[Max]; 31 int l[Max],r[Max]; 32 int time; 33 int fa[Max]; 34 int n,m; 35 36 int min(int a,int b) 37 { 38 return a<b?a:b; 39 } 40 41 void dfs(int root) 42 { 43 l[root]=time++; 44 minChild[root][0]=minChild[root][1]=minDown[root]=n+1; 45 for(int i=head[root];i!=-1;i=en[i].next) 46 { 47 int v=en[i].ad; 48 if(l[v]==0) 49 { 50 fa[v]=root; 51 if(minChild[root][0]>v) 52 { 53 minChild[root][1]=minChild[root][0]; 54 minChild[root][0]=v; 55 } 56 else if(minChild[root][1]>v) minChild[root][1]=v; 57 dfs(v); 58 minDown[root]=min(minDown[root],minDown[v]); 59 } 60 } 61 minDown[root]=min(minDown[root],minChild[root][0]); 62 r[root]=time++; 63 } 64 65 bool isDown(int x,int y) 66 { 67 return l[x]>=l[y]&&r[x]<=r[y]; 68 } 69 70 int main() 71 { 72 int t,a,b; 73 scanf("%d",&t); 74 while(t--) 75 { 76 scanf("%d%d",&n,&m); 77 time=1; 78 eno=0; 79 for(int i=1;i<=n;i++) 80 head[i]=-1; 81 for(int i=1;i<n;i++) 82 { 83 scanf("%d%d",&a,&b); 84 Add(a,b); 85 } 86 memset(l,0,sizeof(l)); 87 memset(r,0,sizeof(r)); 88 dfs(1); 89 int min1=n+1,min2=n+1,son1=n+1,son2=n+1; 90 for(int i=head[1];i!=-1;i=en[i].next) 91 { 92 int v=en[i].ad; 93 int mi=min(v,minDown[v]); 94 if(mi<min1) 95 { 96 min2=min1; 97 son2=son1; 98 min1=mi; 99 son1=v; 100 } 101 else if(mi<min2) 102 { 103 min2=mi; 104 son2=v; 105 } 106 } 107 108 for(int i=0;i<m;i++) 109 { 110 int ans1,ans2; 111 scanf("%d%d",&a,&b); 112 if(b==1) 113 { 114 if(isDown(a,minChild[b][0])) 115 { 116 ans1=minChild[b][1]; 117 } 118 else ans1=minChild[b][0]; 119 if(isDown(a,son1)) 120 { 121 ans2=min2; 122 } 123 else ans2=min1; 124 } 125 else 126 { 127 if(isDown(a,b)) 128 { 129 ans1=isDown(a,minChild[b][0])?minChild[b][1]:minChild[b][0]; 130 ans1=min(fa[b],ans1); 131 ans2=1; 132 } 133 else 134 { 135 ans1=minChild[b][0]; 136 ans2=minDown[b]; 137 } 138 } 139 if(ans1==n+1 || ans2==n+1) 140 { 141 printf("no answers!\n"); 142 } 143 else printf("%d %d\n",ans1,ans2); 144 } 145 printf("\n"); 146 } 147 return 0; 148 }