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!
");
142       }
143       else printf("%d %d
",ans1,ans2);
144     }
145     printf("
");
146   }
147   return 0;
148 }
原文地址:https://www.cnblogs.com/hchlqlz-oj-mrj/p/5413964.html