ural1752找树上距某个点某距离的点(树的直径+倍增)

http://acm.timus.ru/problem.aspx?space=1&num=1752

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 vi and 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.

题意:

树上某个点,问到她距离是k的点是哪个

tip:

找到树的直径,有结论:任意一个点到树上的最远点是直径的两个端点之一,那么k如果比这个距离远,就说明没有这个点,不然就是端点之一往上走(长度-k)或者这个点往上走k,倍增走。
直径:bfs两次,证明的话,分为相交与否两种情况反证一下

#include <cstdio>
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 20000+10;
int n,m,u,v,root,high[maxn],father[maxn][25],first[maxn],tot;
bool vis[maxn];
queue<int>q;
struct node{
    int v,next;
}edges[2*maxn];

void add(int u,int v){
    edges[tot].v = v;edges[tot].next = first[u];first[u] = tot++;
    edges[tot].v = u;edges[tot].next = first[v];first[v] = tot++;
}

void init(){
    memset(first,-1,sizeof(first));
    tot = 0;
    for(int i = 1; i < n ; i++){
        scanf("%d%d",&u,&v);
        add(u,v);
    }
    root = 1;
}

void dfs(int root,int fa,int step){
    high[root] = step;
    for(int k = first[root] ; k != -1; k = edges[k].next){
        if(edges[k].v == fa)    continue;
        father[edges[k].v][0] = root;
        dfs(edges[k].v,root,step+1);
    }
}
void bz(){
    for(int j = 1; j <= 20 ;j++)
        for(int i = 1; i<= n ;i++)
            if(father[i][j-1]!=-1)
                father[i][j] = father[father[i][j-1]][j-1];
            else
                father[i][j] = -1;
}
int bfs(){
    int tmp;
    while(!q.empty()){
        tmp = q.front();
        q.pop();
        for(int  k = first[tmp];k !=-1;k = edges[k].next){
            if(!vis[edges[k].v]){
                vis[edges[k].v] = true;
                q.push(edges[k].v);
            }
        }
    }
    return tmp;
}

void sov(){
    memset(vis,false,sizeof(vis));
    vis[1] = true;
    q.push(1);
    v = bfs();
    memset(vis,false,sizeof(vis));
    vis[v] = true;
    q.push(v);
    u = bfs();
}

int fin(int a,int h){
    for(int i = 0 ;i < 20 ;i++)
        if((1<<i) & h) a = father[a][i];
    return a;
}

int lca(int x,int y){
    if(high[x] < high[y])
        swap(x,y);
    int dc = high[x]-high[y];
    for(int i = 0 ;i < 20 ;i++)
        if((1<<i) & dc) x = father[x][i];
    if(x == y)  return x;
    for(int i = 19 ; i >= 0 ; i--)
        if(father[x][i] != father[y][i]){
            x = father[x][i];
            y = father[y][i];
        }
    x = father[x][0];
    return x;
}

void getq(){
    for(int i = 1; i <= m ;i++){
        int node,k;
        scanf("%d%d",&node,&k);
        int ans = -1,lun = -1,lvn = -1;
        if(high[node]+high[u]-2*high[lun = lca(u,node)] >= k ){
            if(high[node]-high[lun] >=k)
                ans = fin(node,k);
            else
                ans = fin(u,high[node]+high[u]-2*high[lun]-k);
        }
        else if(high[node]+high[v]-2*high[lvn = lca(v,node)] >= k){
            if(high[node]-high[lvn] >=k)
                ans = fin(node,k);
            else
                ans = fin(v,high[node]+high[v]-2*high[lvn]-k);
        }
        else{
            ans = 0;
        }
        printf("%d\n",ans);
    }
}

int main(){
    while(~scanf("%d%d",&n,&m)){
        init();
        sov();
        dfs(1,-1,1);
        bz();
        getq();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值