[BZOJ4129]-Haruna’s Breakfast-带修改树上莫队+分块

说在前面

Emmmmm….和预想差不多,两个半小时AC此题=w=
很久没有写过带修改莫队了,正好也练习一下树上莫队,然后就去码了这题…me的代码真是长到了一种境界
这道题的数据还是很不错的,让me找出了板子里一些细节错误


题目

BZOJ4129传送门

题目大意

给出一棵有N个节点的树,节点有各自的权值,权值均为非负整数。现在需要维护以下操作:
1. 0 u x :将u节点的点权修改为x
2. 1 u v :询问u到v路径上,没有出现过的最小非负整数
数据范围:N和M均不超过50000
时限:40 seconds

输入输出格式

输入格式:
第一行两个整数N,M,表示节点数和操作数
接下来N-1行,每行两个数u v,描述一条树边
再接下来M行,每行三个数,描述一个操作,格式如题

输出格式:
对于每个询问操作,输出权值


解法

OS:看到 这么小 的数据范围,这么 宽松 的时间….肯定是用莫队直接秒掉嘛 QwQ

具体做法:
关于「树上分块」,参见 BZOJ1089 王室联邦
关于「树上莫队」,参见 SPOJ COT2
关于「带修改莫队」,参见 BZOJ2120 数颜色
关于「用莫队+分块求最小未出现数」,参见 BZOJ3585 mex


下面是自带大长度的代码

/**************************************************************
    Problem: 4129
    User: Izumihanako
    Language: C++
    Result: Accepted
    Time:924 ms
    Memory:10780 kb
****************************************************************/

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

int N , M , tp , head[50005] ;
int val[50005] , mcnt , qcnt ;
int Bsiz , Btot , bel[50005] ;
int fa[16][50005] , dep[50005] , ans[50005] ;

struct Modifies{
    int pos , val , pre , tim ; 
}m[50005] ;

struct Queries{
    int u , v , tim ;
    bool operator < ( const Queries &A ) const {
        return bel[u] < bel[A.u] ||
            ( bel[u] == bel[A.u] && bel[v] < bel[A.v] ) ||
            ( bel[u] == bel[A.u] && bel[v] == bel[A.v] && tim < A.tim ) ;
    }
}q[50005] ;

struct num_Block{
    int Bsiz_ , Btot_ , cnt_[50005] ;
    int bel_[50005] , st_[350] , ed_[350] , fullsiz_[350] , Bcnt_[350] ;
    void init(){
        Bsiz_ = sqrt( N ) ;
        Btot_ = ( N / Bsiz_ ) + ( bool )( N %Bsiz_ ) ;
        for( int i = 1 ; i <= Btot_ ; i ++ ){
            st_[i] = ed_[i-1] + 1 ;
            ed_[i] = min( st_[i] + Bsiz_ - 1 , N ) ;
            fullsiz_[i] = ed_[i] - st_[i] + 1 ;
            for( int j = st_[i] ; j <= ed_[i] ; j ++ )
                bel_[j] = i ;
        }
        st_[Btot_+1] = N + 1 ;
        ed_[Btot_+1] = 0x3f3f3f3f ;
        fullsiz_[Btot_+1] = 0x3f3f3f3f - N ;
    }
    void Insert( int x ){
        if( x > N ) return ;
        cnt_[x] ++ ;
        if( cnt_[x] == 1 ) Bcnt_[ bel_[x] ] ++ ;
    }
    void Erase( int x ){
        if( x > N ) return ;
        cnt_[x] -- ;
        if( cnt_[x] == 0 ) Bcnt_[ bel_[x] ] -- ;
    }
    int Query(){
        int tmp ;
        for( int i = 1 ; i <= Btot_ + 1 ; i ++ )
            if( Bcnt_[i] != fullsiz_[i] ){ tmp = i ; break ; }
        for( int i = st_[tmp] ; i <= ed_[tmp] ; i ++ )
            if( !cnt_[i] ) return i ;
    }
}B ;

struct Path{
    int pre , to ;
}p[100005] ;

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

int sta[50005] , topp ;
void dfs( int u ){
    int las = topp ;
    for( int i = head[u] ; i ; i = p[i].pre ){
        int v = p[i].to ;
        if( v == fa[0][u] ) continue ;
        fa[0][v] = u ;
        dep[v] = dep[u] + 1 ;
        dfs( v ) ;
        if( topp - las >= Bsiz ){
            Btot ++ ;
            while( topp != las ) bel[ sta[topp--] ] = Btot ;
        }
    }
    sta[++topp] = u ;
}

void get_ST(){
    for( int i = 1 ; i <= 15 ; i ++ )
        for( int j = 1 ; j <= N ; j ++ )
            fa[i][j] = fa[i-1][ fa[i-1][j] ] ;
}

int Lca( int u , int v ){
    if( dep[u] < dep[v] ) swap( u , v ) ;
    int t = dep[u] - dep[v] , x = 0 ;
    while( t ){
        if( t&1 ) u = fa[x][u] ;
        t >>= 1 , x ++ ;
    }
    if( u == v ) return u ;
    for( int i = 15 ; i >= 0 ; i -- )
        if( fa[i][u] != fa[i][v] )
            u = fa[i][u] , v = fa[i][v] ;
    return fa[0][u] ;
}

void init(){
    Bsiz = (int) pow( N , 2.0 / 3.0 ) ;
    dfs( 1 ) ; fa[0][1] = 1 ; 
    get_ST() ;
    if( topp ){
        Btot ++ ;
        while( topp ) bel[ sta[topp--] ] = Btot ;
    }
    B.init() ;
    sort( q + 1 , q + qcnt + 1 ) ;
}

bool vis[50005] ;
void Change( int u , int x ){
    if( vis[u] ) B.Erase( val[u] ) ;
    val[u] = x ;
    if( vis[u] ) B.Insert( val[u] ) ;
}

void Xor( int u ){
    vis[u] ^= 1 ;
    if( !vis[u] ) B.Erase( val[u] ) ;
    else          B.Insert( val[u] ) ;
}

void Move( int u , int v ){
    if( dep[u] < dep[v] ) swap( u , v ) ;
    while( dep[u] > dep[v] ){
        Xor( u ) ;
        u = fa[0][u] ;
    }
    while( u != v ){
        Xor( u ) ; Xor( v ) ;
        u = fa[0][u] , v = fa[0][v] ;
    }
}

void solve(){
    int nowU = 1 , nowV = 1 , T = 1 ;
    for( int i = 1 ; i <= qcnt ; i ++ ){
        int u = q[i].u , v = q[i].v , tim = q[i].tim , LCA = Lca( u , v ) ;
        while( m[T].tim < tim ){
            Change( m[T].pos , m[T].val ) ;
            T ++ ;
        }
        while( m[T-1].tim > tim ){
            Change( m[T-1].pos , m[T-1].pre ) ;
            T -- ;
        }
        Move( nowU , u ) ; nowU = u ;
        Move( nowV , v ) ; nowV = v ;
        Xor( LCA ) ;
        ans[tim] = B.Query() ;
        Xor( LCA ) ;
    }
    for( int i = 1 ; i <= M ; i ++ )
        if( ans[i] ) printf( "%d\n" , ans[i] - 1 ) ;
}

inline void read_( int &x ){
    x = 0 ;//传引用, 需要清空值
    char ch = getchar() ;
    while( ch < '0' || ch > '9' ) ch = getchar() ;
    while( ch >='0' && ch <='9' ) x = ( x << 1 ) + ( x << 3 ) + ch - '0' , ch = getchar() ;
}

int main(){
    scanf( "%d%d" , &N , &M ) ;
    for( int i = 1 ; i <= N ; i ++ )
        read_( val[i] ) , val[i] ++ ;//权值全部+1, 最后答案-1, 方便分块
    for( int i = 1 , u , v ; i < N ; i ++ ){
        read_( u ) ; read_( v ) ;
        In( u , v ) ; In( v , u ) ;
    }
    for( int i = 1 , opt , u , v ; i <= M ; i ++ ){
        read_( opt ) , read_( u ) , read_( v ) ;
        if( opt == 0 )
            m[++mcnt] = ( Modifies ){ u , v + 1 , val[u] , i } , val[u] = v + 1 ;// Notice here !!!
        else
            q[++qcnt] = ( Queries ){ u , v , i } ;
    }
    m[mcnt+1].tim = 0x3f3f3f3f ;
    for( int i = mcnt ; i ; i -- )
        val[ m[i].pos ] = m[i].pre ;
    init() ;
    solve() ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值