[BZOJ4009]-[HNOI2015]接水果-dfs序+扫描线+整体二分

说在前面

这题的代码真是恶心到me了…


题目

BZOJ4009传送门

题目大意

给出一棵有N个节点的树。再给出一个路径集合,集合里的元素是一个三元组(u,v,val),表示这是一条从u到v的路径,定义该条路径为val。现在给出一些询问形如(u,v,k),表示询问集合中路径是u到v的子路径,且权值第k小的元素的权值。
节点数,集合大小,询问数均不超过40000

输入输出格式

输入格式:
第一行三个整数N,P,Q,表示节点数,集合大小和询问个数
接下来N-1行,每行一个二元组(u,v),描述一条树边
再接下来P行,每行一个三元组(u,v,val)描述一个集合元素,含义如题
再接下来Q行,每行一个三元组(u,v,k)描述一个询问,含义如题

输出格式:
对于每个询问,输出一行一个数字,表示答案


解法

这道题用到了一个很妙的处理方式,把路径的包含问题转化了
根据题意,给出一条路径P,要求出集合中是P的子路径,且权值第k小的那个。正向找出符合条件的子路径是很麻烦的,于是倒着来,看集合里哪些元素能对该答案造成贡献。
假设子路径为(u,v),P为(x,y),设in[u],out[u]分别表示u的dfs序。
有两种情况,分类讨论一下:

  • 当u是v的祖先时,设w是u的一个儿子,且v在w的子树里。此时需要一个点在v的子树里,另一个点在w的子树外,这样的话路径(x,y)就包含了路径(u,v)。写成不等式的形式:1≤in[x]≤in[w]-1 && in[v] ≤ in[y] ≤ out[v] 或者 in[v] ≤ in[y] ≤ out[v] && out[w]+1 ≤ in[x] ≤ N。示意图如下:
    这里写图片描述
  • 当u不是v的祖先时,此时需要一个点在u的子树里,一个点在v的子树里。写成不等式的形式:in[u] ≤ in[x] ≤ out[u] && in[v] ≤ in[y] ≤ out[v]。示意图就不画了,比较简单。

发现两种情况实际上都是二维区间第k大,于是可以使用树套树解决。
当然有一种更简单恶心的写法,就是把k大转判定问题,使用扫描线+二分答案完成此题,常数应该会小一些


下面是自带大长度的代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int N , P , Q , tp , head[40005] , fa[16][40005] , lcnt , maxv , minv = 1e9 , ans[40005] ;
struct Path{
    int pre , to ;
}p[80005] ;

struct options{
    int x , y1 , y2 , val , delta ;
    bool operator < ( const options &A ) const {
        return x < A.x ;
    }
}o[160005] , Lo[160005] , Ro[160005] ;

struct Queries{
    int x , y , K , id ;
    bool operator < ( const Queries &A ) const {
        return x < A.x ;
    }
    Queries operator - ( const int emm ) const {
        return ( Queries ){ x , y , K - emm , id } ;
    }
}q[40005] , Lq[40005] , Rq[40005] ;

struct BIT{
    int b[40005] ;
    void Modify( int x , int delta ){
        for( ; x <= N ; x += x&-x )
            b[x] += delta ;
    }
    void Modify( int x , int y , int delta ){
        Modify( y + 1 , -delta ) ; Modify( x , delta ) ;
    }
    int Query( int x ){
        int rt = 0 ;
        for( ; x ; x -= x&-x )
            rt += b[x] ;
        return rt ;
    }
} B ;

void In( int t1 , int t2 ){
    p[++tp].pre = head[t1] ;
    p[ head[t1] = tp ].to = t2 ;
}

void setMat( int x1 , int x2 , int y1 , int y2 , int val ){
    o[++lcnt] = ( options ){ x1 , y1 , y2 , val , 1 } ;
    o[++lcnt] = ( options ){ x2 + 1 , y1 , y2 , val , -1 } ;
}

int dep[40005] , in[40005] , out[40005] , dfs_c ;
void dfs( int u ){
    in[u] = ++dfs_c ;
    for( int i = head[u] ; i ; i = p[i].pre ){
        int v = p[i].to ;
        if( v == fa[0][u] ) continue ;
        dep[v] = dep[u] + 1 ;
        fa[0][v] = u , dfs( v ) ;
    } out[u] = dfs_c ;
}

int getNxt( int u , int v ){
    for( int i = 15 ; i >= 0 ; i -- )
        if( dep[ fa[i][v] ] > dep[u] ) v = fa[i][v] ;
    return v ;
}

void preWork(){
    fa[0][1] = 1 ; dfs( 1 ) ;
    for( int i = 1 ; i <= 15 ; i ++ )
        for( int j = 1 ; j <= N ; j ++ )
            fa[i][j] = fa[i-1][ fa[i-1][j] ] ;
    for( int i = 1 , u , v , val ; i <= P ; i ++ ){
        scanf( "%d%d%d" , &u , &v , &val ) ;
        minv = min( minv , val ) , maxv = max( maxv , val ) ;
        if( in[u] > in[v] ) swap( u , v ) ;
        if( in[u] < in[v] && out[v] <= out[u] ){
            int w = getNxt( u , v ) ;
            setMat( 1 , in[w] - 1 , in[v] , out[v] , val ) ;
            if( out[w] < N ) setMat( in[v] , out[v] , out[w] + 1 , N , val ) ;
        } else setMat( in[u] , out[u] , in[v] , out[v] , val ) ;
    } sort( o + 1 , o + lcnt + 1 ) ;
    for( int i = 1 , u , v , k ; i <= Q ; i ++ ){
        scanf( "%d%d%d" , &u , &v , &k ) ;
        if( in[u] > in[v] ) swap( u , v ) ;
        q[i] = ( Queries ){ in[u] , in[v] , k , i } ;
    } sort( q + 1 , q + Q + 1 ) ;
}

void CDQ( int opL , int opR , int qL , int qR , int vL , int vR ){
    if( vL == vR ){
        for( int i = qL ; i <= qR ; i ++ )
            ans[ q[i].id ] = vL ;
        return ;
    }
    int mid = ( vL + vR ) >> 1 , opLt = 0 , opRt = 0 , qLt = 0 , qRt = 0 , pt1 , pt2 ;
    for( pt1 = opL , pt2 = qL ; pt2 <= qR ; pt2 ++ ){
        while( pt1 <= opR && o[pt1].x <= q[pt2].x ){
            if( o[pt1].val <= mid ){
                Lo[++opLt] = o[pt1] ;
                B.Modify( o[pt1].y1 , o[pt1].y2 , o[pt1].delta ) ;
            } else Ro[++opRt] = o[pt1] ;
            pt1 ++ ;
        }
        int tmp = B.Query( q[pt2].y ) ;
        if( tmp >= q[pt2].K ) Lq[++qLt] = q[pt2] ;
        else Rq[++qRt] = q[pt2] - tmp ;
    }
    for( int i = 1 ; i <= opLt ; i ++ )
        B.Modify( Lo[i].y1 , Lo[i].y2 , -Lo[i].delta ) ;
    while( pt1 <= opR ){
        if( o[pt1].val <= mid ) Lo[++opLt] = o[pt1] ;
        else Ro[++opRt] = o[pt1] ;
        pt1 ++ ;
    }
    memcpy( o + opL , Lo + 1 , opLt * sizeof( options ) ) ;
    memcpy( o + opL + opLt , Ro + 1 , opRt * sizeof( options ) ) ;
    memcpy( q + qL , Lq + 1 , qLt * sizeof( Queries ) ) ;
    memcpy( q + qL + qLt , Rq + 1 , qRt * sizeof( Queries ) ) ;
    if( qLt ) CDQ( opL , opL + opLt - 1 , qL , qL + qLt - 1 , vL , mid ) ;
    if( qRt ) CDQ( opR - opRt + 1 , opR , qR - qRt + 1 , qR , mid+1,vR ) ;
}

void solve(){
    CDQ( 1 , lcnt , 1 , Q , minv , maxv ) ;
    for( int i = 1 ; i <= Q ; i ++ )
        printf( "%d\n" , ans[i] ) ;
}

int main(){
    scanf( "%d%d%d" , &N , &P , &Q ) ;
    for( int i = 1 , u , v ; i < N ; i ++ ){
        scanf( "%d%d" , &u , &v ) ;
        In( u , v ) ; In( v , u ) ;
    }
    preWork() ;
    solve() ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值