说在前面
早上考试考到这道题
没想一会想到一个
nlog4n
n
log
4
n
的做法,感觉药丸
然后听见出题人小声一句:
log4n
log
4
n
是对的。嗯,然后开始码码码
真·长,还被数据卡精度emmmm
题目
题目大意
给出一棵
n
n
个节点的树,每个节点有四个权值:
现在需要回答
m
m
个询问,每个询问大概长这样:在 两点的链上,选出两个点
i,j
i
,
j
,使得
yi+qixi+pi
y
i
+
q
i
x
i
+
p
i
最大
范围:
n,m≤3∗104
n
,
m
≤
3
∗
10
4
,
xi, yi, pi, qi≤105
x
i
,
y
i
,
p
i
,
q
i
≤
10
5
约定:输出误差不能超过
10−3
10
−
3
输入输出格式
输入格式:
第一行一个正整数
n
n
,含义如题
第二,三,四,五行分别包含 个正实数。第
i
i
个数分别表示表示
接下来
n−1
n
−
1
行,每行两个正整数
a,b
a
,
b
,描述一条树边
接下来一行包含一个数
m
m
,表示询问的个数
最后 行,每行包含正整数
u,v
u
,
v
,表示一次询问
输出格式:
对于每个询问,输出一行一个实数表示答案qwq
解法
把式子一化,发现是个 类似分数规划的形式,然后显然二分答案
然后二分的那个值看起来就和斜率一样,所以式子变成了 同一个斜率的线 在两个凸包上切得的最大值之和
然后就用线段树维护凸包就好了…每个节点开一个vector,当前凸包上的点一定是两个儿子凸包上的点,归并一下即可
注意我们不需要合并信息,因为我们要的是切凸包最大值,这个大凸包一定是由覆盖到的区间的小凸包上的点组成,所以我们在查询时候,把各个覆盖到的区间的凸包都询问一遍就好
下面是代码
#include <ctime>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
//#define Izumihanako
using namespace std ;
double eps = 1e-9 , MAXK = 0 ;
int N , M , tp , head[30005] , stt ;
struct Point{
double x , y ;
} p1[30005] , p2[30005] , t[100005] ;
struct Path{
int pre , to ;
} p[60005] ;
typedef Point Vector ;
Vector operator - ( const Vector &A , const Vector &B ){ return ( Vector ){ A.x - B.x , A.y - B.y } ; }
double cross( const Vector &A , const Vector &B ){ return A.x * B.y - A.y * B.x ; }
double slope( const Vector &A ){ return A.y / A.x ; }
int sign( const double &x ){
if( x < eps && x > -eps ) return 0 ;
return x > eps ? 1 : -1 ;
}
void In( int t1 , int t2 ){
p[++tp] = ( Path ){ head[t1] , t2 } ; head[t1] = tp ;
p[++tp] = ( Path ){ head[t2] , t1 } ; head[t2] = tp ;
}
void smax( double &x , double y ){ if( x < y ) x = y ; }
struct Data{
double v1 , v2 ;
inline void update( const Data &A ){
if( v1 < A.v1 ) v1 = A.v1 ;
if( v2 < A.v2 ) v2 = A.v2 ;
}
} ;
struct Node{
Node *ch[2] ;
int siz1 , siz2 ;
vector<Point> cvx1 , cvx2 ;
void Merge( vector<Point> &L , vector<Point> &R ){
stt = 0 ;
int Ls = L.size() , Rs = R.size() , lp = 0 , rp = 0 ;
while( lp < Ls || rp < Rs ){
if( rp == Rs || ( lp != Ls && L[lp].x < R[rp].x ) )
t[++stt] = L[lp] , lp ++ ;
else t[++stt] = R[rp] , rp ++ ;
}
}
void calcu_cvx(){
int ba = -1 ; this->Merge( ch[0]->cvx1 , ch[1]->cvx1 ) ;
for( int i = 1 ; i <= stt ; i ++ ){
while( ba >= 1 && sign( cross( cvx1[ba] - cvx1[ba-1] , t[i] - cvx1[ba-1] ) ) >= 0 )
cvx1.pop_back() , ba -- ;
cvx1.push_back( t[i] ) , ba ++ ;
} while( ba >= 1 && sign( cvx1[ba].y - cvx1[ba-1].y ) <= 0 ) cvx1.pop_back() , ba -- ;
siz1 = ba + 1 ;
ba = -1 , this->Merge( ch[0]->cvx2 , ch[1]->cvx2 ) ;
for( int i = 1 ; i <= stt ; i ++ ){
while( ba >= 1 && sign( cross( cvx2[ba] - cvx2[ba-1] , t[i] - cvx2[ba-1] ) ) >= 0 )
cvx2.pop_back() , ba -- ;
cvx2.push_back( t[i] ) , ba ++ ;
} while( ba >= 1 && sign( cvx2[ba].y - cvx2[ba-1].y ) <= 0 ) cvx2.pop_back() , ba -- ;
siz2 = ba + 1 ;
}
} *root , w[60005] , *tw = w ;
int siz[30005] , dep[30005] , son[30005] , fa[30005] ;
int in[30005] , arc[30005] , dfs_c , top[30005] ;
void dfs1( int u ){
for( int i = head[u] ; i ; i = p[i].pre ){
int v = p[i].to ;
if( v == fa[u] ) continue ;
fa[v] = u , dep[v] = dep[u] + 1 ;
dfs1( v ) , siz[u] += siz[v] ;
if( siz[ son[u] ] < siz[v] ) son[u] = v ;
} siz[u] ++ ;
}
void dfs2( int u , int tp ){
in[u] = ++dfs_c , arc[dfs_c] = u , top[u] = tp ;
if( son[u] ) dfs2( son[u] , tp ) ;
for( int i = head[u] ; i ; i = p[i].pre ){
int v = p[i].to ;
if( v == fa[u] || v == son[u] ) continue ;
dfs2( v , v ) ;
}
}
Node *build( int lf , int rg ){
Node *nd = ++tw ;
if( lf == rg ){
nd->cvx1.push_back( p1[ arc[lf] ] ) ;
nd->cvx2.push_back( p2[ arc[lf] ] ) ;
nd->siz1 = nd->siz2 = 1 ; return nd ;
} int mid = ( lf + rg ) >> 1 ;
nd->ch[0] = build( lf , mid ) ;
nd->ch[1] = build( mid+1,rg ) ;
nd->calcu_cvx() ; return nd ;
}
double K ; int L , R ;
double cut_cvx( vector<Point> &cvx , int siz ){
int lf = 0 , rg = siz - 2 , ans = siz - 1 , mid ;
while( lf <= rg ){
mid = ( lf + rg ) >> 1 ;
double slope = ::slope( cvx[mid+1] - cvx[mid] ) ;
if( sign( K - slope ) > 0 ) ans = mid , rg = mid - 1 ;
else lf = mid + 1 ;
} return cvx[ans].y - cvx[ans].x * K ;
}
Data Query( Node *nd , int lf , int rg ){
if( L <= lf && rg <= R )
return ( Data ){ cut_cvx( nd->cvx1 , nd->siz1 ) , cut_cvx( nd->cvx2 , nd->siz2 ) } ;
int mid = ( lf + rg ) >> 1 ; Data rt = ( Data ){ -1e20 , -1e20 } ;
if( L <= mid ) rt.update( Query( nd->ch[0] , lf , mid ) ) ;
if( R > mid ) rt.update( Query( nd->ch[1] , mid+1,rg ) ) ;
return rt ;
}
double Jump( int u , int v ){
Data rt = ( Data ){ -1e20 , -1e20 } ;
while( top[u] != top[v] ){
if( dep[ top[u] ] < dep[ top[v] ] ) swap( u , v ) ;
L = in[ top[u] ] , R = in[u] , rt.update( Query( root , 1 , N ) ) ;
u = fa[ top[u] ] ;
} if( dep[u] < dep[v] ) swap( u , v ) ;
L = in[v] , R = in[u] , rt.update( Query( root , 1 , N ) ) ;
return rt.v1 + rt.v2 ;
}
double qvq( int u , int v ){
double lf = 0 , rg = MAXK , res ;
while( rg - lf >= 1e-4 ){
K = ( lf + rg ) / 2 ;
res = Jump( u , v ) ;
if( sign( res ) > 0 ) lf = K ;
else rg = K ;
} return ( lf + rg ) / 2 ;
}
void preWork(){
dfs1( 1 ) , dfs2( 1 , 1 ) ;
root = build( 1 , N ) ;
}
void solve(){
scanf( "%d" , &M ) ;
for( int i = 1 , u , v ; i <= M ; i ++ ){
scanf( "%d%d" , &u , &v ) ;
printf( "%f\n" , qvq( u , v ) ) ;
}
}
int main(){
#ifdef Izumihanako
freopen( "out.txt", "w" , stdout) ;
freopen( "in.txt" , "r" , stdin ) ;
#endif
scanf( "%d" , &N ) ;
for( int i = 1 ; i <= N ; i ++ ) scanf( "%lf" , &p1[i].x ) ;
for( int i = 1 ; i <= N ; i ++ ) scanf( "%lf" , &p1[i].y ) , smax( MAXK , p1[i].y / p1[i].x ) ;
for( int i = 1 ; i <= N ; i ++ ) scanf( "%lf" , &p2[i].x ) ;
for( int i = 1 ; i <= N ; i ++ ) scanf( "%lf" , &p2[i].y ) , smax( MAXK , p2[i].y / p2[i].x ) ;
/*
for( int i = 1 ; i <= N ; i ++ ){
scanf( "%lf%lf" , &p1[i].x , &p1[i].y ) , smax( MAXK , p1[i].y / p1[i].x ) ;
scanf( "%lf%lf" , &p2[i].x , &p2[i].y ) , smax( MAXK , p2[i].y / p2[i].x ) ;
}
*/
for( int i = 1 , u , v ; i < N ; i ++ )
scanf( "%d%d" , &u , &v ) , In( u , v ) ;
preWork() ; solve() ;
}