[BZOJ2402]-陶陶的難題II-二分答案+線段樹上凸包

說在前面

早上考試考到這道題
沒想一會想到一個nlog4n 的做法,感覺藥丸
然後聽見出題人小聲一句:log4n 是對的。嗯,然後開始碼碼碼
真·長,還被數據卡精度emmmm


題目

BZOJ2402傳送門

題目大意

給出一棵 n 個節點的樹,每個節點有四個權值:xi  yi  pi  qi
現在需要回答 m 個詢問,每個詢問大概長這樣:在 u,v 兩點的鏈上,選出兩個點 i,j ,使得 yi+qixi+pi 最大
範圍:n,m3104xi, yi, pi, qi105
約定:輸出誤差不能超過103

輸入輸出格式

輸入格式:
第一行一個正整數 n ,含義如題
第二,三,四,五行分別包含 n 個正實數。第 i 個數分別表示表示 xi, yi, pi, qi

接下來 n1 行,每行兩個正整數 a,b ,描述一條樹邊
接下來一行包含一個數 m ,表示詢問的個數
最後 m 行,每行包含正整數 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() ;
}

發佈了268 篇原創文章 · 獲贊 29 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章