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 ; }