BZOJ5188: [Usaco2018 Jan]MooTube 并查集+离线处理

BZOJ又不给题面...

Luogu的翻译看不下去...

题意简述

有一个$n$个节点的树,边有权值,定义两个节点之间的距离为两点之间的路径上的最小边权

给你$Q$个询问,问你与点$v$的距离超过$k$的点有多少个

$n,Q<=100000$

题解

很妙的做法。

并查集+离线

显然可以把询问离线,按K值排序

处理距离的话可以使用并查集,并不需要带权,只需要把边也按权值排序,用并查集维护。

具体做法:对每个点维护一个$siz$数组表示与它联通的节点数目,用类似双指针的方法把符合规则的边的两端点并起来,$siz$也顺便并起来就好

对于每个询问的答案就是把大于k的边并起来之后的$siz[v]-1$

#include<set>
#include<cstdio>
#include<algorithm>

#define ll long long
#define inf 0x3f3f3f3f 
#define il inline 

namespace io {

    #define in(a) a=read()
    #define out(a) write(a)
    #define outn(a) out(a),putchar('
')

    #define I_int int 
    inline I_int read() {
        I_int x = 0 , f = 1 ; char c = getchar() ;
        while( c < '0' || c > '9' ) { if( c == '-' ) f = -1 ; c = getchar() ; } 
        while( c >= '0' && c <= '9' ) { x = x * 10 + c - '0' ; c = getchar() ; } 
        return x * f ;
    } 
    char F[ 200 ] ;
    inline void write( I_int x ) {
        I_int tmp = x > 0 ? x : -x ;
        if( x < 0 ) putchar( '-' ) ;
        int cnt = 0 ;
        while( tmp > 0 ) {
            F[ cnt ++ ] = tmp % 10 + '0' ;
            tmp /= 10 ;
        }
        while( cnt > 0 ) putchar( F[ -- cnt ] ) ;
    }
    #undef I_int

}
using namespace io ;

using namespace std ;

#define N 100010

struct edge {
    int u , v , w ;
    bool operator < ( const edge &x ) const {
        return w > x.w ;
    }
} a[ N ] ;

struct node {
    int k , v , id ;
    bool operator < ( const node &x ) const {
        return k > x.k ;
    }
} b[ N ] ;

int f[ N ] , siz[ N ] ;
int n , m , ans[ N ] ;

int find( int x ) {
    if( f[ x ] == x ) return x ;
    else return f[ x ] = find( f[ x ] ) ;
}

int main() {
    n = read() ; m = read() ;
    for( int i = 1 ; i < n ; i ++ ) {
        a[ i ] = (edge) { read() , read() , read() } ;
    }
    for( int i = 1 ; i <= m ; i ++ ) {
        b[ i ] = (node) { read() , read() , i } ;
    }
    sort( a + 1 , a + n + 1 ) ; sort( b + 1 , b + m + 1 ) ;
    int j = 1 ;
    for( int i = 1 ; i <= n ; i ++ ) siz[ i ] = 1 , f[ i ] = i ;
    for( int i = 1 ; i <= m ; i ++ ) {
        while( j < n && a[ j ].w >= b[ i ].k ) {
            int x = find( a[ j ].u ) , y = find( a[ j ].v ) ;
            if( x != y ) {
                f[ y ] = x ;
                siz[ x ] += siz[ y ] ;
            }
            j ++ ;
        }
        ans[ b[ i ].id ] = siz[ find( b[ i ].v ) ] - 1 ;
    }
    for( int i = 1 ; i <= m ; i ++ ) {
        printf( "%d
" , ans[ i ] ) ;
    }
    return 0 ;
}