[BZOJ4285]-使者-dfs序+CDQ

说在前面

me发现me的代码简直冗杂的一比…
虽然跑得快,但是写起来简直要死人


题目

BZOJ4285传送门

题目大意

为什么这题是权限题啊woc,me懒得概括题意啊喂!
给出一棵N个节点的树。除了树边之外,这棵树上的某两个点还可能出现传送门。初始状态时,树上已经有一些传送门。
现在需要支持三种操作:
1. 添加一个链接(u,v)的传送门
2. 崩塌一个链接(u,v)的传送门(保证存在)
3. 询问从u到v且不经过「原树(没有传送门)上u到v路径中任意一条边」的路径有多少条
数据范围:节点数100000,操作总数50000,初始传送门50000

输入输出格式

输入格式:
第一行一个整数N,表示节点个数
接下来N-1行,每行一个二元组(u,v)描述一条树边
接下来一行一个整数M,表示初始传送门个数
接下来M行,每行一个二元组(u,v)描述一个传送门
接下来一行一个整数Q,表示操作个数
接下来Q行,每行一个三元组(opt,u,v)描述一个操作,opt与题面对应

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


解法

这道题的原题面有巨大的理解歧义,差评
首先把样例图画出来,然后发现自己读懂了题意
可以发现,在一个询问中,一个传送门最多只会贡献1的答案,当且仅u和v都在这个传送门和树边形成的环上
也就是原树上,u到v的路径被传送门端点间的路径完全包含,然后这道题就和BZOJ4009一样了

即可以将该题转化为:支持插入点和删除点,询问一个矩形内的点数
把每个矩形询问 差分为 四个前缀和询问,然后直接用CDQ三维偏序解决即可。

注意这里的三位偏序是不严格的,询问点和插入点可能重合,一点小改动即可处理


下面是自带小常数的代码

/**************************************************************
    Problem: 4285
    User: Izumihanako
    Language: C++
    Result: Accepted
    Time:6900 ms
    Memory:42352 kb
****************************************************************/
 
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
 
int N , M , Q , tp , head[100005] , ans[50005] , ocnt , qcnt , Qcnt ;
struct Path{
    int pre , to ;
}p[200005] ;
struct queries{
    int x , y , k , id , tim ;
}q[400005] , Lq[400005] , Rq[400005] ;
struct options{
    int x , y , delta , tim ;
}o[100005] , Lo[100005] , Ro[100005] ;
 
struct BIT{
    int b[100005] ;
    void Modify( int x , int delta ){
        for( ; x <= N ; x += x&-x )
            b[x] += delta ;
    }
    int Query( int x ){
        int rt = 0 ;
        for( ; x ; x -= x&-x )
            rt += b[x] ;
        return rt ;
    }
} B ;
 
void setMat( int x1 , int x2 , int y1 , int y2 , int i ){
    q[++qcnt] = ( queries ){ x2 , y2 , 1 , Qcnt , i } ;
    q[++qcnt] = ( queries ){ x2 , y1-1 , -1 , Qcnt , i } ;
    q[++qcnt] = ( queries ){ x1-1 , y2 , -1 , Qcnt , i } ;
    q[++qcnt] = ( queries ){ x1-1 , y1-1 , 1 , Qcnt , i } ;
}
 
void In( int t1 , int t2 ){
    p[++tp].pre = head[t1] ;
    p[ head[t1] = tp ].to = t2 ;
}
 
int dep[100005] , fa[17][100005] , dfs_c , in[100005] , out[100005] ;
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 getW( int u , int v ){
    for( int i = 16 ; 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 <= 16 ; i ++ )
        for( int j = 1 ; j <= N ; j ++ )
            fa[i][j] = fa[i-1][ fa[i-1][j] ] ;
    scanf( "%d" , &M ) ;
    for( int i = 1 , u , v ; i <= M ; i ++ ){
        scanf( "%d%d" , &u , &v ) ;
        if( in[u] > in[v] ) swap( u , v ) ;
        o[++ocnt] = ( options ){ in[u] , in[v] , 1 , 0 } ;
    }
    scanf( "%d" , &Q ) ;
    for( int i = 1 , ct , u , v ; i <= Q ; i ++ ){
        scanf( "%d%d%d" , &ct , &u , &v ) ;
        if( in[u] > in[v] ) swap( u , v ) ;
        if( ct == 1 ) o[++ocnt] = ( options ){ in[u] , in[v] , 1 , i } ;
        if( ct == 2 ) o[++ocnt] = ( options ){ in[u] , in[v] ,-1 , i } ;
        if( ct == 3 ){
            Qcnt ++ ;
            if( in[u] < in[v] && out[v] <= out[u] ){
                int w = getW( u , v ) ;
                setMat( 1 , in[w] - 1 , in[v] , out[v] , i ) ;
                if( out[w] < N ) setMat( in[v] , out[v] , out[w] + 1 , N , i ) ;
            } else setMat( in[u] , out[u] , in[v] , out[v] , i ) ;
        }
    }
}
 
void CDQ( int oL , int oR , int qL , int qR , int xL , int xR ){
    int mid = ( xL + xR ) >> 1 , Lqt = 0 , Rqt = 0 , Lot = 0 , Rot = 0 , pt1 , pt2 ;
    for( pt1 = oL , pt2 = qL ; pt2 <= qR ; pt2 ++ ){
        while( pt1 <= oR && o[pt1].tim <= q[pt2].tim ){
            if( o[pt1].x <= mid ){
                B.Modify( o[pt1].y , o[pt1].delta ) ;
                Lo[++Lot] = o[pt1++] ;
            } else Ro[++Rot] = o[pt1++] ;
        }
        if( q[pt2].x > mid || xL == xR ){
            ans[ q[pt2].id ] += q[pt2].k * B.Query( q[pt2].y ) ;
            Rq[++Rqt] = q[pt2] ;
        } else Lq[++Lqt] = q[pt2] ;
    }
    for( int i = 1 ; i <= Lot ; i ++ ) B.Modify( Lo[i].y , -Lo[i].delta ) ;
    if( xL == xR ) return ;
    while( pt1 <= oR )
        if( o[pt1].x <= mid ) Lo[++Lot] = o[pt1++] ;
        else Ro[++Rot] = o[pt1++] ;
    memcpy( o + oL , Lo + 1 , Lot * sizeof( options ) ) ;
    memcpy( o + oL + Lot , Ro + 1 , Rot * sizeof( options ) ) ;
    memcpy( q + qL , Lq + 1 , Lqt * sizeof( queries ) ) ;
    memcpy( q + qL + Lqt , Rq + 1 , Rqt * sizeof( queries ) ) ;
    if( Lqt && Lot ) CDQ( oL , oL + Lot - 1 , qL , qL + Lqt - 1 , xL , mid ) ;
    if( Rqt && Rot ) CDQ( oR - Rot + 1 , oR , qR - Rqt + 1 , qR , mid+1,xR ) ;
}
 
void solve(){
    CDQ( 1 , ocnt , 1 , qcnt , 0 , N ) ;
    for( int i = 1 ; i <= Qcnt ; i ++ )
        printf( "%d\n" , ans[i] ) ;
}
 
int main(){
    scanf( "%d" , &N ) ;
    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、付费专栏及课程。

余额充值