[SPOJ COT2]-Count on a tree II-树上莫队

说在前面

乘着这几天大力填补之前留下的坑…
树上分块果然还是使用 王室联邦法 最好了=w=


题目

SPOJ COT2传送门

题目大意

给定一棵有N个节点的树,树上的节点都有各自的权值
现在给出M个询问,询问u到v的路径上有多少种不同的权值

输入输出格式

输入格式:
第一行两个整数N,M
接下来N-1行,每行两个整数表示树边连接的点
再接下来M行,每行两个数u,v表示询问

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


解法

(像这种树剖和LCT没法维护的,果然还是用莫队好了=w=)
树分块呢,大概有这样的一些方法:
王室联邦分块法:可以保证每个块的大小和直径都不超过 2N1 ,但是不保证块联通
DFS序分块法:首先是好写(毕竟转化成了序列问题),严格保证块大小 N ,但是不保证直径,也不保证联通。处理子树信息比较方便
size分块:检查当前节点的父亲所在块的大小,如果小于 N 就把当前节点加入进去,不然新开块。块大小最坏 N ,保证块内联通,还保证直径,多么优美啊=w=,可惜不能保证块个数(一个菊花图就死了恍恍惚惚哈哈哈哈)….

于是这道题呢,采用王室联邦分块法
维护的是两个端点到根的路径上的 贡献的异或。于是假设当前点为nowu,nowv,那么当前就应该维护下了nowu到nowv路径上的答案(不包含LCA,因为LCA会被异或两遍)。在查答案的时候单独加上LCA的贡献就可以了
关于修改,假设下一个询问的点为aimu,aimv,那么就把nowu挪动到aimu去,nowv挪动到aimv去,这一步可以通过暴力跳它们的父亲来实现(因为每个块大小直径都有保证,所以总的复杂度也是可以保证的)。


下面是自带大常数的代码

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

int N , M , tp , head[40005] ;
int dfn[40005] , dfs_c , val[40005] ;
int uninum , bel[40005] , Bsiz , Btot , ans[100005] ;

struct Unique_Data{
    int num , id ;
    bool operator < ( const Unique_Data &A ) const {
        return num < A.num ;
    }
}Uni[40005] ;

struct Queries{
    int u , v , id ;
    bool operator < ( const Queries &A ) const {
        return ( bel[u] < bel[A.u] ) ||
            ( bel[u] == bel[A.u] && dfn[v] < dfn[A.v] ) ;
    }
}Q[100005] ;

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

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

void Unique_(){
    sort( Uni + 1 , Uni + N + 1 ) ;
    for( int i = 1 , las = -2e9 ; i <= N ; i ++ ){
        if( las != Uni[i].num ){
            uninum ++ ;
            las = Uni[i].num ;
        }
        val[ Uni[i].id ] = uninum ;
    }
}

int dep[40005] , fa[16][40005] , sta[40005] , topp ;
void dfs( int u ){
    dfn[u] = ++dfs_c ;
    int last = topp ;
    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 ) ;
        if( topp - last >= Bsiz ){
            Btot ++ ;
            while( topp != last ) bel[ sta[topp--] ] = Btot ;
        }
    }
    sta[++topp] = u ;
}

void getST(){
    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(){
    Unique_() ;
    Bsiz = sqrt( N ) ;
    dfs( 1 ) ; fa[0][1] = 1 ;
    if( topp ){
        Btot ++ ;
        while( topp ) bel[ sta[topp--] ] = Btot ;
    }
    sort( Q + 1 , Q + M + 1 ) ;
    getST() ;
}

bool vis[40005] ;
int cnt[40005] , nowans ;
void Xor( int x ){
    if( vis[x] ){
        vis[x] = false ;
        if( -- cnt[ val[x] ] == 0 ) nowans -- ;
    } else {
        vis[x] = true ;
        if( ++ cnt[ val[x] ] == 1 ) nowans ++ ;
    }
}

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 ;
    for( register int i = 1 ; i <= M ; i ++ ){
        int aimu = Q[i].u , aimv = Q[i].v , LCA = Lca( aimu , aimv ) ;
        Move_( nowu , aimu ) ; nowu = aimu ;
        Move_( nowv , aimv ) ; nowv = aimv ;
        Xor( LCA ) ;
        ans[ Q[i].id ] = nowans ;
        Xor( LCA ) ;
    }
    for( register int i = 1 ; i <= M ; i ++ )
        printf( "%d\n" , ans[i] ) ;
}

inline void read_( int &x ){
    int flag = 1 ; x = 0 ;
    char ch = getchar() ;
    while( ch < '0' || ch > '9' ){ if( ch == '-' ) flag = -1 ; ch = getchar() ; }
    while( ch >='0' && ch <='9' ) x = ( x << 1 ) + ( x << 3 ) + ch - '0' , ch = getchar() ;
    x *= flag ;
}

int main(){
    scanf( "%d%d" , &N , &M ) ;
    for( register int i = 1 ; i <= N ; i ++ ){
        read_( val[i] ) ;
        Uni[i].num = val[i] , Uni[i].id = i ;
    }
    for( int i = 1 , u = 0 , v = 0 ; i < N ; i ++ ){
        read_( u ) , read_( v ) ;
        In( u , v ) ; In( v , u ) ;
    }
    for( register int i = 1 ; i <= M ; i ++ ){
        read_( Q[i].u ) , read_( Q[i].v ) ;
        Q[i].id = i ;
    }
    init() ;
    solve() ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值