hdu 4008 Parent and son


/*
首先以1为根,扫描一遍树,得到每个节点的minchild[i][2],儿子节点的最小值和次小值(不同子树的)
和每个节点的最小后缀的值mindown[i]
(1)如果x是y的父节点的话,那么直接输出mindown[i],minchild[i][0]即可
(2)如果y是x的父节点
【1】当y不为1的时候
     对于第一问      找到y到x这条路径上离y最近的节点z,若z=minchild[0],输出minchild[1]和y的父亲节点的最小值,
                     否则输出minchild[0]和y的父亲节点的最小值
     对于第二问      输出1
【2】当y是1的时候(预先处理出他的最小和次小的后缀值mindown[0],mindown[1])
     对于第一问      找到y到x这条路径上离y最近的节点z,若z=minchild[0],输出minchild[1],否则输出minchild[0]
     对于第二问      找到y到x这条路径上离y最近的节点z的最小后缀值(包括z),和mindown[0]比较,
                      不同输出mindown[1],否则输出mindown[0]
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 100000 + 1234;
struct EDGE
{
    int u, v;
} edge[MAXN * 2];
int cnt;
int minchild[MAXN][2];
int mindown[MAXN];
int fa[MAXN][20];
int head[MAXN];
int n;
int depth[MAXN], maxdepth, topk;
bool cmp(EDGE a, EDGE b)
{
    if(a. u != b.u)  return a.u < b.u;
    return a.v < b.v;
}
void insert(int *s, int x)
{
    if(x < s[0])
    {
        s[1] = s[0];
        s[0] = x;
    }
    else if(x < s[1])  s[1] = x;
}
void dfs(int root)
{
    minchild[root][0] = minchild[root][1] = mindown[root] = n + 1;//赋为最大值
    for(int p = head[root]; p < cnt && edge[p].u == root; p++)
    {
        int v = edge[p].v;
        if(v == fa[root][0])  continue;//访问到了父节点
        fa[v][0] = root;
        insert(minchild[root], v);
        depth[v] = depth[root] + 1;
        dfs(v);
        mindown[root] = min(mindown[root], mindown[v]);
    }
    maxdepth = max(maxdepth, depth[root]);
    mindown[root] = min(mindown[root], minchild[root][0]);
}
int findfather(int x, int step)
{
    int k = topk - 1;
    while(step > 0)
    {
        while((1 << k) > step) k--;
        x = fa[x][k];
        step -= (1 << k);
    }
    return x;
}
int main()
{
    int T;
    int q, x, y;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d", &n, &q);
        cnt = n - 1;
        for(int i = 0; i < cnt; i++)
        {
            scanf("%d%d", &edge[i].u, &edge[i].v);
            edge[i + cnt].u = edge[i].v;
            edge[i + cnt].v = edge[i].u;
        }
        cnt = cnt + cnt;
        sort(edge, edge + cnt, cmp);
        head[1] = 0;
        for(int i = 1; i < cnt; i++)
            if(edge[i].u != edge[i-1].u)  head[edge[i].u] = i;

        fa[1][0] = 0;
        depth[1] = maxdepth = 0;
        dfs(1);

        topk = 0;
        while((1 << topk) <= maxdepth)  topk++;
        for(int k = 1; k < topk; k++)
            for(int i = 1; i <= n; i++)
                if(fa[i][k - 1] != 0)  fa[i][k] = fa[fa[i][k-1]][k-1];

        int mindown1[2];
        mindown1[0] = mindown1[1] = n + 1;
        for(int p = head[1]; p < cnt && edge[p].u == 1; p++)
        {
            int v = edge[p].v;
            int rv = min(mindown[v], v);
            insert(mindown1, rv);
        }

        while(q--)
        {
            scanf("%d%d", &x, &y);//不会出现x==y的情况
            int ans1 = n + 1, ans2 =n + 1;
            if(y == 1)
            {
                int z = findfather(x, depth[x] - depth[y] - 1);
                int rz = min(z, mindown[z]);
                ans1 = (z == minchild[1][0] ? minchild[1][1] : minchild[1][0]);
                ans2 = (rz == mindown1[0] ? mindown1[1] : mindown1[0]);
            }
            else
            {
                bool flag = false;
                int z;
                if(depth[y] < depth[x])
                {
                    z = findfather(x, depth[x] - depth[y] - 1);
                    if(fa[z][0] == y)  flag = true;
                }
                if(flag)
                {
                    //printf("z = %d\n", z);
                    //printf("fa=%d\n", fa[y][0]);
                    ans1 = (z == minchild[y][0] ? min(minchild[y][1], fa[y][0]) : min(minchild[y][0], fa[y][0]));
                    ans2 = 1;
                }
                else
                {
                    ans1 = minchild[y][0];
                    ans2 = mindown[y];
                }
            }
            if (ans1 > n) printf("no answers!\n");
            else printf("%d %d\n", ans1, ans2);
        }
        puts("");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值