前提知识点:
树的直径,及记录方法:从图中任取一点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 ;
}