树形DP+LCA+思路。这题可真是有点难度......所以准备详细写一下题解。

题意:给一颗无根树,有Q次询问,每次询问指定一个根节点X,然后让你计算Y节点的儿子和子孙中,编号最小的节点是多少。

我们先以1为根节点进行一次树形DP,记录下每个节点的儿子和子孙中,编号最小的节点是多少。

首先很容易想到一种情况:就是X和Y的最近公共祖先不是Y,这个时候,结果和以1为根节点建树是一模一样的,因为把X提到最上面去,不会影响到Y的子树的情况。

剩余的情况就是X和Y的最近公共祖先等于Y(这种情况就是X在Y的子树上),这个时候,如果把X提上去,那么无法直接从以1为根节点DP的结果得出答案,我们需要进行推导。这个时候,需要分两种情况。若Y==1,这个时候把X提上去,就好比从1连出来那么多的节点的子树中,X所在子树不再是Y的后代,如果X所在子树是最小值存在的子树,那么输出次小值,否则输出最小值。若Y!=1,这个时候,也是X所在子树不成为Y的后代,那么依然需要知道X所在子树是否是最小值存在的子树,还需要知道Y的父亲的情况,因为这个时候,Y的父亲那边的所有点成为了Y的子树。

综上所述,树形DP进行DFS的时候,我们需要记录每个子树的儿子最小值、次小值,后代最小值、次小值,每个节点的父亲节点的编号。
还需要o(1)判断两点是否在一条链上,这个可以通过时间戳来判断。

有了上述信息,在进行完先以1为根节点的DP之后,每一次询问,都可以在o(1)内得到答案。

代码写的又臭又长....还是不要看了。

#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <queue>
#include <stack>
#include <map>
#include <vector>
using namespace std; const int maxn=+;
const int INF=0x7fffffff; int n,m;
vector<int>tree[maxn];
bool vis[maxn];
int MinSon[maxn],CiSon[maxn];
int MinDes[maxn],CiDes[maxn];
int belong[maxn];
int Fa;
int MinSonPos;
int MinDesPos;
int CiSonPos;
int CiDesPos;
struct P
{
int a,b;
}pp[maxn];
bool cmp(const P&a,const P&b) { return a.b<b.b;} int par[maxn];
int dfnIn[maxn],dfnOut[maxn];
int time;
int fa[maxn],l[maxn],p[maxn][];
int top, sun;
struct Edge{
int v;
Edge* next;
}*adj[maxn], edge[maxn << ]; void add(int u, int v){
Edge* p = &edge[++top];
p -> v = v;
p -> next = adj[u];
adj[u] = p;
}
void DFS(int u, int father, int depth){ fa[u] = father; l[u] = depth;
for(Edge* p = adj[u]; p; p = p -> next){
int v = p -> v;
if(v == father) continue; DFS(v, u, depth + ); }
}
void init_p(int n){
int log = ;
for(; ( << log) < n; log++); log--;
for(int i = ; i <= n; i++){
for(int j = ; j <= log; j++) p[i][j] = -;
}
for(int i = ; i <= n; i++) p[i][] = fa[i];
for(int j = ; j <= log; j++){
for(int i = ; i <= n; i++) if(p[i][j - ] != -) p[i][j] = p[p[i][j - ]][j - ];
}
}
int query(int a, int b){
int log, i;
if(l[a] < l[b]) swap(a, b);
for(log = ; ( << log) <= l[a]; log++); log--;
for(i = log; i >= ; i--){
if(l[a] - ( << i) > l[b]){
a = p[a][i];
}
}
sun = a;
if(fa[a] == b) return b;
a = fa[a];
for(i = log; i >= ; i--)
if(p[a][i] != - && p[a][i] != p[b][i]){
a = p[a][i]; b = p[b][i];
}
return fa[a];
} void init()
{
top = ;time=;
for(int i = ; i <= n; i++) adj[i] = ;
memset(vis,,sizeof vis);
for(int i=;i<=n;i++) MinSon[i]=INF,MinDes[i]=INF;
for(int i=;i<=n;i++) CiSon[i]=INF,CiDes[i]=INF;
for(int i=;i<=n;i++) tree[i].clear();
} void read()
{
for(int i=; i<n; i++)
{
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
tree[u].push_back(v);
tree[v].push_back(u);
}
} void dfs(int now)
{
dfnIn[now]=++time;
if(now!=) belong[now]=Fa; bool fail=;
for(int i=;i<tree[now].size();i++)
if(!vis[tree[now][i]]) fail=; if(fail)
{
dfnOut[now]=++time;
return;
}
int *ff= new int[tree[now].size()+];
for(int i=;i<tree[now].size();i++) ff[i]=;
for(int i=;i<tree[now].size();i++)
{
if(vis[tree[now][i]]) continue;
ff[i]=; par[tree[now][i]]=now;
if(now==) Fa=tree[now][i];
vis[tree[now][i]]=;
dfs(tree[now][i]);
MinSon[now]=min(MinSon[now],tree[now][i]);
MinDes[now]=min(MinDes[now],MinDes[tree[now][i]]);
}
MinDes[now]=min(MinDes[now],MinSon[now]); for(int i=;i<tree[now].size();i++)
{
if(!ff[i]) continue;
if(tree[now][i]!=MinSon[now])
CiSon[now]=min(CiSon[now],tree[now][i]); if(MinDes[tree[now][i]]!=MinDes[now])
CiDes[now]=min(CiDes[now],MinDes[tree[now][i]]); if(CiDes[tree[now][i]]!=MinDes[now])
CiDes[now]=min(CiDes[now],CiDes[tree[now][i]]); }
if(MinSon[now]!=MinDes[now])
CiDes[now]=min(CiDes[now],MinSon[now]); dfnOut[now]=++time; delete []ff;
} void work()
{
memset(vis,,sizeof vis);
DFS(, -, );
init_p(n);
vis[]=;dfs(); MinSonPos=INF; MinDesPos=INF;
CiSonPos=INF; CiDesPos=INF; for(int i=;i<tree[].size();i++)
MinSonPos=min(MinSonPos,tree[][i]); for(int i=;i<tree[].size();i++)
{
if(tree[][i]==MinSonPos) continue;
CiSonPos=min(CiSonPos,tree[][i]);
} int yy=;
for(int i=;i<tree[].size();i++)
{
pp[yy].a=tree[][i];
pp[yy++].b=min(tree[][i],MinDes[tree[][i]]);
}
sort(pp,pp+yy,cmp);
MinDesPos=pp[].a;
if(yy>=) CiDesPos=pp[].a; for(int i=;i<=m;i++)
{
int X,Y;
scanf("%d%d",&X,&Y);
int U=query(X,Y); if(U!=Y)
{
if(MinDes[Y]==INF) printf("no answers!\n");
else printf("%d %d\n",MinSon[Y],MinDes[Y]);
} else
{
if(Y==)
{
int ans1,ans2;
int be=belong[X];
if(MinSonPos==be) ans1=CiSonPos;
else ans1=MinSonPos;
if(MinDesPos==be) ans2=CiDesPos;
else ans2=MinDesPos;
if(ans1==INF||ans2==INF) printf("no answers!\n");
else printf("%d %d\n",ans1,min(ans2,MinDes[ans2]));
}
else
{
int ans=INF;
if(dfnIn[X]>=dfnIn[MinSon[Y]]&&dfnOut[X]<=dfnOut[MinSon[Y]]) ans=min(CiSon[Y],par[Y]);
else if(dfnIn[MinSon[Y]]>=dfnIn[X]&&dfnOut[MinSon[Y]]<=dfnOut[X]) ans=min(CiSon[Y],par[Y]);
else ans=min(MinSon[Y],par[Y]);
printf("%d 1\n",ans);
}
}
}
} int main()
{
int T; scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
init();
read();
work();
printf("\n");
}
return ;
}

HDU 4008 Parent and son的更多相关文章

  1. HDU 4008 Parent and son LCA+树形dp

    题意: 给定case数 给定n个点的树,m个询问 以下n-1行给出树边 m个询问 x y 问:以x为根.y子树下 y的最小点标的儿子节点 和子孙节点 思路: 用son[u][0] 表示u的最小儿子 s ...

  2. HDU4008 Parent and son(树形DP LCA)

    先记录以1为根时每个节点子树儿子节点的最大与次小值,询问x, y时,先判断x在不在y的子树范围内,若不在,结果为y的儿子结点,后继的最小值. 若x在y的子树范围内,若y儿子最小值是x的前驱,从次小值与 ...

  3. hdu 4008 树形dp

    思路:我们定义一个dfn[i],Maxndfn[i]来确定节点i的访问次序,以及其子节点的最大访问次序.那么另一个节点是其子树的节点当且仅当dfn[j]>=dfn[i]&&dfn ...

  4. Parent and son

    Give you a tree with N vertices and N‐ 1 edges, and then ask you Q queries on “which vertex is Y's s ...

  5. [c++] Smart Pointers

    内存管理方面的知识 基础实例: #include <iostream> #include <stack> #include <memory> using names ...

  6. Python之路【第十八篇】Django小项目简单BBS论坛部分内容知识点

    开发一个简单的BBS论坛 项目需求: 整体参考“抽屉新热榜” + “虎嗅网” 实现不同论坛版块 帖子列表展示 帖子评论数.点赞数展示 在线用户展示 允许登录用户发贴.评论.点赞 允许上传文件 帖子可被 ...

  7. python 学习笔记二十 django项目bbs论坛

    项目:开发一个简单的BBS论坛 需求: 整体参考“抽屉新热榜” + “虎嗅网” 实现不同论坛版块 帖子列表展示 帖子评论数.点赞数展示 在线用户展示 允许登录用户发贴.评论.点赞 允许上传文件 帖子可 ...

  8. Java(接口与继承)动手动脑

    1>继承条件下的构造方法调用 运行 TestInherits.java 示例,观察输出,注意总结父类与子类之间构造方法的调用关系修改 Parent 构造方法的代码,显式调用 GrandParen ...

  9. JS魔法堂:判断节点位置关系

    一.前言 在polyfill querySelectorAll 和写弹出窗时都需要判断两个节点间的位置关系,通过jQuery我们可以轻松搞定,但原生JS呢?下面我将整理各种判断方法,以供日后查阅. 二 ...

随机推荐

  1. LightOJ 1259 Goldbach`s Conjecture 素数打表

    题目大意:求讲一个整数n分解为两个素数的方案数. 题目思路:素数打表,后遍历 1-n/2,寻找方案数,需要注意的是:C/C++中 bool类型占用一个字节,int类型占用4个字节,在素数打表中采用bo ...

  2. CDialog上使用CToolBar+CReBar

    最经在做一些用户界面的东西,对话框上有很多按钮和组合框,全部加起来差不多有20多个吧,界面非常凌乱,最后决定用CToolBar + CReBar来重新设计界面,为什么选用这个呢?一是因为看到IE用的也 ...

  3. 字段为空sql语句,设置当前模式

    delete from t_corpinfo  where CORPID='' and CORPNAME=''  该命令是删除字段为空的记录 SET CURRENT SCHEMA DB2INST1;

  4. codeforces--376D--376F(前缀和优化)

    time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standa ...

  5. android经典开源代码集合

    一.依赖注入DI通过依赖注入减少View.服务.资源简化初始化,事件绑定等重复繁琐工作1. AndroidAnnotations(Code Diet) android快速开发框架项目地址:https: ...

  6. 【转载】最全的面试题目整理(HTML+CSS部分)

    转载自 知乎 @西点王子 https://www.zhihu.com/people/F211/answers 1. 常用那几种浏览器测试?有哪些内核(Layout Engine)? (Q1) 浏览器: ...

  7. .NET中公共变量与属性的区别

    在我们的程序中经常会出现以下的代码:  如:     成员变量     public   string   Name;     或者用属性     private   string   name    ...

  8. emacs format

    格式化源码是很常见的需求,emacs有个indent-region函数用于格式化选定的代码,前提是你处在某个非text mode下,如c-mode或者java-mode之类.如果要格式化整个文件,你需 ...

  9. C#入门经典(第五章-1)

  10. listView上拉刷新下拉加载

    xlistview_header.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLa ...