說在前面
早上考試考到這道題
沒想一會想到一個 的做法,感覺藥丸
然後聽見出題人小聲一句: 是對的。嗯,然後開始碼碼碼
真·長,還被數據卡精度emmmm
題目
題目大意
給出一棵 個節點的樹,每個節點有四個權值:
現在需要回答 個詢問,每個詢問大概長這樣:在 兩點的鏈上,選出兩個點 ,使得 最大
範圍: ,
約定:輸出誤差不能超過
輸入輸出格式
輸入格式:
第一行一個正整數 ,含義如題
第二,三,四,五行分別包含 個正實數。第 個數分別表示表示
接下來 行,每行兩個正整數 ,描述一條樹邊
接下來一行包含一個數 ,表示詢問的個數
最後 行,每行包含正整數 ,表示一次詢問
輸出格式:
對於每個詢問,輸出一行一個實數表示答案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() ;
}