2017acm/icpc北京赛区网络赛 Agent Communication 二分+搜索

6 篇文章 0 订阅
5 篇文章 0 订阅


前提知识点:

树的直径,及记录方法:从图中任取一点dfs,搜到最远点为起点,再进行一次搜索,搜到最远点为终点

记录:从起点dfs,搜的过程存入数组,搜到终点返回


题意: 可以在树中连一条边,求连边之后的最长路的最短值,(所有的最大的最小或最小的最大都要考虑下二分)。

易得,连边的起点和终点一定在树的一条直径上。

预处理出所有点间的距离,(由于是树,搜索就可以了)

求出任意一条直径并记录,然后求出所有点到这条直径(链)的距离。

设连边的起点为x,终点为y , 任意两点a, b

然后二分mid

即只要满足| x - a | + | y - b | <= mid  即可以成立

拆绝对值符号,可以得到关于 x + y , x - y 的两组关系, 只要使 X, y , 存在即可, 所以只要使大于号一边的大于小于号一边的

另外注意要使等式成立要特判当相等是奇偶的情况。

这里二分的具体分析与证明参照此文 传送门 :   http://blog.csdn.net/tc_to_top/article/details/51476095

其他的分析写成注释在代码里了

嗯  , 第一次做的话是道非常好的题目  


can you hear me  ? 

#include <bits/stdc++.h>
using namespace std ;
const int inf = 1e9 + 7 ;
const int N = 1405 ;
int n  , St , Ed , len;
int d[N] ;///the path of the diameter
int p[N] ;///the point on the path is other point s parent
int dis[N] ;///the distance to the parent
int length[N][N] ;///all length
bool flag , vis[N] ;///point on path
vector<int> G[N] ;///edge
///all point s length between each other
void set_dis(int v , int fa , int head , int deep){
    length[head][v] = deep ;
    for(int k = 0 ; k < G[v].size() ; k ++ )
        if(G[v][k] != fa) set_dis(G[v][k] , v , head , deep + 1) ;
}
///search the tree s diameter
void dfs_diameter(int u , int fa , int deep , int & t){
    if(deep > len ){ len = deep , t = u ; }
    for(int v = 0 ; v < G[u].size() ; v ++ )
        if(G[u][v] != fa ) dfs_diameter(G[u][v] , u , deep+1 , t) ;
}
///record the path of the diameter
void dfs_path(int u , int fa , int deep){
    d[deep] = u ;
    if(u == Ed){flag = true ; return ;}
    for(int v = 0 ; v < G[u].size() ; v ++)
        if(!flag && G[u][v] != fa) dfs_path(G[u][v] , u , deep + 1) ;
}
///record the point which are not on the diameter  the distance to the diameter
void dfs_other_point(int u , int fa , int deep , int head){
    p[u] = head ; /// the point to the point on the diameter
    dis[u] = deep ;/// the distance to the diameter
    for(int v = 0 ; v < G[u].size() ; v ++)
        if(G[u][v] != fa && !vis[G[u][v]]) dfs_other_point(G[u][v] , u , deep + 1 , head) ;
}
///the core , judge
bool check (int mid ){
    int ma1 = -inf , ma2 = -inf , mi1 = inf , mi2 = inf ;
    for(int i = 1 ; i < n ; i ++ ){
        for(int j = i + 1 ; j <= n ; j ++ ){
            if(length[i][j] <= mid) continue ;
            int x = i , y = j , temp = dis[i] + dis[j] + 1 ;
            if(p[x] > p[y]) swap(x , y) ;
            ma1 = max(p[x] + p[y] - (mid - temp) , ma1) ;
            ma2 = max(p[y] - p[x] - (mid - temp) , ma2) ;
            mi1 = min(p[x] + p[y] + mid - temp , mi1) ;
            mi2 = min(p[y] - p[x] + mid - temp , mi2) ;
        }
    }
    if(mi1 < 0 || mi2 < 0 ) return false ;
    if(ma1 > mi1 || ma2 > mi2 ) return false ;
    if(mi1 < ma2) return false ;
    if(ma1 == mi1 && ma2 == mi2 && ((ma1 + ma2) &1)) return false ;
    return true ;
}
int main(){
    int kase ;scanf("%d" ,&kase) ;
    while( kase -- ){
        memset(vis , 0 , sizeof(vis)) ;
        memset(length , 0 , sizeof(length)) ;
        memset(d , 0 , sizeof(d)) ;
        memset(p , 0 , sizeof(p)) ;
        memset(dis , 0 , sizeof(dis)) ;
        scanf("%d" , &n) ;
        for(int i = 0 ; i <= n ; i ++ ) G[i].clear() ;
        for(int i = 1 ; i < n ; i ++ ){
            int u , v ;
            scanf("%d %d" , &u , &v ) ;
            G[u].push_back(v) ; G[v].push_back(u) ;
        }
        for(int i = 1 ; i <= n ; i ++ ) set_dis(i , 0 , i , 0) ;
        len = 0 ;
        dfs_diameter(1 , 0 , 1 , St) ;
        len = 0 ; ///the diameter ' s length
        dfs_diameter(St , 0 , 1 , Ed) ;
        flag = false ;
        dfs_path(St , 0 , 1) ;

        for(int i = 1 ; i <= len ; i ++) vis[d[i]] = true ;
        for(int i = 1 ; i <= len ; i ++) dfs_other_point(d[i] , 0 , 0 , i) ; 
        /// watch out , the forth element is i , not d[i] , as there will be a new chain with new numbers 
        /// as to ensure the length of the chain is only len         
        int l = 0 , r = n ;
        while( l != r ){
            int mid = (l + r) >> 1 ;
            if(check(mid)) r = mid ;
            else l = mid + 1 ;
        }
        printf("%d\n" , l) ;
    }
    return 0 ;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值