多校联合练习赛1 Problem1008 Park Visit 树状图最长直径

73 篇文章 21 订阅
56 篇文章 0 订阅

Park Visit

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 0    Accepted Submission(s): 0


Problem Description
  
  
Claire and her little friend, ykwd, are travelling in Shevchenko's Park! The park is beautiful - but large, indeed. N feature spots in the park are connected by exactly (N-1) undirected paths, and Claire is too tired to visit all of them. After consideration, she decides to visit only K spots among them. She takes out a map of the park, and luckily, finds that there're entrances at each feature spot! Claire wants to choose an entrance, and find a way of visit to minimize the distance she has to walk. For convenience, we can assume the length of all paths are 1. Claire is too tired. Can you help her?
 

Input
  
  
An integer T(T≤20) will exist in the first line of input, indicating the number of test cases. Each test case begins with two integers N and M(1≤N,M≤10 5), which respectively denotes the number of nodes and queries. The following (N-1) lines, each with a pair of integers (u,v), describe the tree edges. The following M lines, each with an integer K(1≤K≤N), describe the queries. The nodes are labeled from 1 to N.
 

Output
  
  
For each query, output the minimum walking distance, one per line.
 

Sample Input
  
  
1 4 2 3 2 1 2 4 2 2 4
 

Sample Output
  
  
1 4
 

Source
  
  
2013 Multi-University Training Contest 1
 


题解:

首先会发现这是一个树状图,因为边只有n-1条,且各点都能抵达。 然后你再仔细计算,会发现如果我们要走最少的路,就是让走一趟的路最多,走两趟的路最少,然后你通过计算后会发现,支路上有几个点,你来回就是它的倍数,所以我们要做的就是让他能走一条主干最长的路,这样在支路上你走的就是最少的(你可以走一半就走回来的) 所以,题目转化为求树状图的直径问题。


其算法为:
任选一个入度与出度为1的u作为起点,对树进行BFS遍历,找出离u最远的点v。然后以v为起点,再进行BFS遍历,找出离v最远的点w。则v到w的路径长度即为树的直径。时间复杂度为O(n)。
 
原理:设起点为u,第一次BFS找到的终点v一定是树的直径的一个端点。
证明:
如果u 是直径上的点,则v必然是直径的终点。(因为如果v不是的话,则必定存在另一个点w使得u到w的距离更长,则于BFS找到了v矛盾)
如果u不是直径上的点,则u到v的路径必然与树的直径相交,设交点为c,那么c到v的路径与直径的后半段重合。所以v一定是直径的一个端点,因此从v进行BFS得到的一定是直径长度。


不过我还是习惯性的用DFS来实现了求距离。。

设直径为d,就是进行下判断

if(k <= d+1) //点的个数比边多1

   ans = k-1;

else ans = (k-d-1)*2+d;


我的代码:

/*
 * @author ipqhjjybj
 * @date  20130723
 *
 */

#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>

#include <iostream>
#include <cmath>
#include <algorithm>

#include <cstring>
#include <vector>
#include <string>
using namespace std;

#define inf 0x3f3f3f3f
#define MAXN 100005
#define clr(x,k) memset((x),(k),sizeof(x))
#define cpy(x,k) memcpy((x),(k),sizeof(x))
#define Base 10000

typedef vector<int> vi;
#define foreach(it,c) for(vi::iterator it = (c).begin();it != (c).end();++it)

#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
int dist[MAXN];
vi Map[MAXN];
bool visit[MAXN];
int n,q;
void dfs(int u,int deep){
    dist[u]=deep;
    visit[u]=true;
    foreach(it,Map[u]){
        if(!visit[*it]){
            dfs(*it,deep+1);
        }
    }
}
int main(){
    //freopen("1008.in","r",stdin);
    int t;
    scanf("%d",&t);
    while(t--){
        scanf("%d %d",&n,&q);
        for(int i = 1;i <=n;i++) Map[i].clear(),dist[i]=-1;
        for(int i = 1,a,b;i < n;i++){
            scanf("%d %d",&a,&b);
            Map[a].push_back(b);
            Map[b].push_back(a);
        }
        clr(visit,false);
        int max=0;
        int ii;
        for(int i = 1;i <= n;i++){
            if(Map[i].size()==1){
                //dist[i]=0;
                ii=i;
                dfs(i,0);
                break;
            }
        }
        for(int i = 1;i <= n;i++){
            if(dist[i]>max){
                ii = i;
                max = dist[i];
            }
        }
        clr(visit,false);
        clr(dist,-1);
        dfs(ii,0);
        max = 0;
        for(int i = 1;i <= n;i++){
            if(dist[i]>max){
                ii = i;
                max = dist[i];
            }
        }
        int ans = max+1;
        for(int i = 0,que;i < q;i++){
            scanf("%d",&que);
            if(que<=ans){
                printf("%d\n",que-1);
            }else{
                printf("%d\n",(que-ans)*2+ans-1);
            }
        }
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值