题目链接
题意
给你一颗带有边权的树,求有多少点对
(
u
,
v
)
(u,v)
( u , v ) 满足从
u
u
u 到
v
v
v 的路径上最多有
l
l
l 条边,总边权最多为
w
w
w
题解
对于经过重心的点对,由于边数大不一定总权值也打,所以两者不同步,考虑先根据边数排序,然后对权值和建一颗主席树,双指针扫一下找到醉的位置满足边总数的要求,然后主席树查询区间小于
w
−
d
i
s
[
i
]
.
a
w-dis[i].a
w − d i s [ i ] . a 的数量,其中
d
i
s
[
i
]
.
a
dis[i].a
d i s [ i ] . a 表示当前枚举的权值和
复杂度
O
(
n
(
log
n
)
2
)
O(n(\log n)^2)
O ( n ( log n ) 2 )
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn= 1e5 + 10 ;
const int maxm= 4e6 + 10 ;
int n, k, l;
namespace president_tree{
int sum[ maxm] , cnt, ls[ maxm] , rs[ maxm] , roo[ maxn] , val[ maxn] , tot;
inline void init ( ) { cnt= 0 ; }
inline int build ( int l, int r) {
int now = ++ cnt;
if ( l == r) return now;
int mid = ( l + r) >> 1 ;
ls[ now] = build ( l, mid) ;
rs[ now] = build ( mid + 1 , r) ;
return now;
}
inline int modify ( int l, int r, int loc, int pre) {
int now= ++ cnt;
sum[ now] = sum[ pre] + 1 ;
if ( l== r) return now;
int mid= ( l+ r) >> 1 ;
if ( loc<= mid) {
rs[ now] = rs[ pre] ;
ls[ now] = modify ( l, mid, loc, ls[ pre] ) ;
} else {
ls[ now] = ls[ pre] ;
rs[ now] = modify ( mid+ 1 , r, loc, rs[ pre] ) ;
}
return now;
}
inline void build_tree ( int a[ ] , int n) {
init ( ) ;
for ( int i= 1 ; i<= n; i++ ) val[ i] = a[ i] ;
sort ( val+ 1 , val+ n+ 1 ) ;
tot= unique ( val+ 1 , val+ n+ 1 ) - val- 1 ;
roo[ 0 ] = build ( 1 , tot) ;
for ( int i= 1 ; i<= n; i++ ) {
int loc= lower_bound ( val+ 1 , val+ tot+ 1 , a[ i] ) - val;
roo[ i] = modify ( 1 , tot, loc, roo[ i- 1 ] ) ;
}
}
inline int query ( int l, int r, int k, int a, int b) {
if ( l== r) return val[ l] <= k? sum[ b] - sum[ a] : 0 ;
if ( l> r) return 0 ;
int mid= ( l+ r) >> 1 ;
if ( k> val[ mid] ) return sum[ ls[ b] ] - sum[ ls[ a] ] + query ( mid+ 1 , r, k, rs[ a] , rs[ b] ) ;
return query ( l, mid, k, ls[ a] , ls[ b] ) ;
}
} using namespace president_tree;
namespace point_divide_and_conquer{
int tot, head[ maxn] , siz[ maxn] , c[ maxn] , root, min_son, num;
bool vis[ maxn] ;
struct data {
int a, b;
data ( int c= 0 , int d= 0 ) { a= c, b= d; }
friend bool operator< ( const data & u, const data & v) {
if ( u. b== v. b) return u. a< v. a;
return u. b< v. b;
}
} dis[ maxn] ;
struct ed{ int v, w, next; } edge[ 2 * maxn] ;
inline void init ( int n) {
tot= 0 ;
for ( int i= 1 ; i<= n; i++ ) head[ i] = 0 , vis[ i] = false;
}
inline void add_edge ( int u, int v, int w) {
edge[ ++ tot] = ed{ v, w, head[ u] } ;
head[ u] = tot;
}
inline void dfs_size ( int cur, int fa) {
siz[ cur] = 1 ;
for ( int i= head[ cur] ; i; i= edge[ i] . next) {
if ( edge[ i] . v!= fa && ! vis[ edge[ i] . v] ) {
dfs_size ( edge[ i] . v, cur) ;
siz[ cur] + = siz[ edge[ i] . v] ;
}
}
}
inline void dfs_root ( int cur, int fa, int all) {
int max_son= all- siz[ cur] ;
for ( int i= head[ cur] ; i; i= edge[ i] . next) {
if ( edge[ i] . v!= fa && ! vis[ edge[ i] . v] ) {
max_son= max ( max_son, siz[ edge[ i] . v] ) ;
dfs_root ( edge[ i] . v, cur, all) ;
}
}
if ( max_son< min_son) min_son= max_son, root= cur;
}
inline void dfs_roote ( int cur, int fa, int d, int h) {
dis[ ++ num] = data ( d, h) ;
for ( int i= head[ cur] ; i; i= edge[ i] . next) {
if ( edge[ i] . v!= fa && ! vis[ edge[ i] . v] ) {
dfs_roote ( edge[ i] . v, cur, d+ edge[ i] . w, h+ 1 ) ;
}
}
}
inline long long calc ( int cur, int fa, int d, int h) {
num= 0 ;
dfs_roote ( cur, fa, d, h) ;
sort ( dis+ 1 , dis+ num+ 1 ) ;
for ( int i= 1 ; i<= num; i++ ) c[ i] = dis[ i] . a;
build_tree ( c, num) ;
long long ans= 0 ; int p= num;
for ( int i= 1 ; i<= num; i++ ) {
while ( p> i && dis[ p] . b+ dis[ i] . b> l) p-- ;
if ( p> i) ans+ = query ( 1 , president_tree: : tot, k- dis[ i] . a, roo[ i] , roo[ p] ) ;
}
return ans;
}
inline long long solve ( int cur) {
min_son= 0x3f3f3f3f ;
dfs_size ( cur, 0 ) ;
dfs_root ( cur, 0 , siz[ cur] ) ;
vis[ root] = true;
long long ans= calc ( root, 0 , 0 , 0 ) ;
for ( int i= head[ root] ; i; i= edge[ i] . next) if ( ! vis[ edge[ i] . v] ) ans- = calc ( edge[ i] . v, 0 , edge[ i] . w, 1 ) ;
for ( int i= head[ root] ; i; i= edge[ i] . next) if ( ! vis[ edge[ i] . v] ) ans+ = solve ( edge[ i] . v) ;
return ans;
}
}
using namespace point_divide_and_conquer;
int main ( ) {
while ( ~ scanf ( "%d %d %d" , & n, & l, & k) && n) {
init ( n) ;
for ( int i= 1 , u, w; i< n; i++ ) {
scanf ( "%d %d" , & u, & w) ;
add_edge ( i+ 1 , u, w) ;
add_edge ( u, i+ 1 , w) ;
}
printf ( "%lld\n" , solve ( 1 ) ) ;
}
}