ural 1750 Tree 2(树的直径 + 倍增)

Time Limit: 1000MS Memory Limit: 65536KB 64bit IO Format: %I64d & %I64u

[]   [Go Back]   [Status]  

Description

Consider a tree consisting of   n  vertices. A   distance  between two vertices is the minimal number of edges in a path connecting them. Given a vertex   vi  and distance   di  find a vertex   ui  such that distance between   viand   ui  equals to   di.

Input

The first line contains the number of vertices   n  (1 ≤   n  ≤ 20000)   and the number of queries   q  (1 ≤   q  ≤ 50000)   . Each of the following   n − 1 lines describes an edge and contains the numbers of vertices connected by this edge. Vertices are numbered from 1 to   n. The next   q  lines describe the queries. Each query is described by a line containing two numbers   vi  (1 ≤   vi  ≤   n)   and   di  (0 ≤   di  ≤   n)   .

Output

You should output   q  lines. The   i-th line should contain a vertex number   ui, the answer to the   i-th query. If there are several possible answers, output any of them. If there are no required vertices, output 0 instead.

Sample Input

input

output

9 101 81 51 42 72 53 65 96 95 48 14 32 49 31 15 23 56 47 3

0123456789

 

 

题意:给出一棵树,树上两点的距离为两点路径上的边数,有q个询问,每个询问u d,要求出与u相距为d的任意一个结点。

思路:先求树的直径,得到直径上的两端点,分别以这两个端点为根能构造出两棵新的树,与任意一个点u距离为d的结点v可以在其中一棵树上通过倍增来求出。

 

AC代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <vector>
#include <algorithm>
#define ll long long
using namespace std;

const int maxn = 20005;
const int INF = 1e9;

int n, q;
vector<int> G[maxn];
int to[maxn];
int dis[2][maxn], fa[maxn][2], anc[maxn][30][2];
bool vis[maxn];
int bfs(int s, int k){
    queue<int> Q;
    memset(vis, 0, sizeof(vis));
    memset(dis[k], 0, sizeof(dis[k]));
    vis[s] = 1;
    Q.push(s);
    int idx = s, Max = -1;
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        if(dis[k][u] > Max)
        {
            Max = dis[k][u];
            idx = u;
        }
        for(int i = 0; i < (int) G[u].size(); i++)
        {
            int v = G[u][i];
            if(vis[v]) continue;
            vis[v] = true;
            dis[k][v] = dis[k][u] + 1;
            fa[v][k] = u;
            Q.push(v);
        }
    }
    for(int i = 1; i <= n; i++)
    to[i] = max(to[i], dis[k][i]);
    return idx;
}
void init(){
    for(int k = 0; k < 2; k++)
    {
        for(int i = 1; i <= n; i++)
        {
            anc[i][0][k] = fa[i][k];
            for(int j = 1; (1 << j) < n; j++)
            anc[i][j][k] = -1;
        }
        for(int j = 1; (1 << j) < n; j++)
        for(int i = 1; i <= n; i++)
        if(anc[i][j - 1][k] != -1)
        {
            int a = anc[i][j - 1][k];
            anc[i][j][k] = anc[a][j - 1][k];
        }
    }
}
int query(int k, int u, int d){
    if(d == 0) return u;
    if(d == 1) return anc[u][0][k];
    int log = 1;
    for(; (1 << log) <= d; log++);
    log--;
    for(int i = log; i >= 0; i--)
    {
        if(d >= (1 << i))
        {
            d -= (1 << i);
            u = anc[u][i][k];
        }
        if(d == 0) return u;
    }
    return 0;
}
int main()
{
    int a, b;
    while(~scanf("%d%d", &n, &q))
    {
        for(int i = 1; i <= n; i++) G[i].clear();
        for(int i = 0; i < n - 1; i++)
        {
            scanf("%d%d", &a, &b);
            G[a].push_back(b);
            G[b].push_back(a);
        }
        memset(to, 0, sizeof(to));
        int s = bfs(1, 0);
        int t = bfs(s, 1);
        bfs(t, 0);
        init();
        while(q--)
        {
            scanf("%d%d", &a, &b);
            if(to[a] < b)
            {
                puts("0");
                continue;
            }
            if(dis[0][a] > dis[1][a]) printf("%d\n", query(0, a, b));
            else printf("%d\n", query(1, a, b));
        }
    }
    return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值