算法分析:
点分治,子树去重类可做。对于一个重心,取出所有路径按照最大值排序,扫下最小值,插入到线段树里维护个数,双指针扫扫解决。然后对所有重心的儿子对应的子树再做一次去重。这个方法我是今早在床上想了下想到的。
我是动态树,动态树比较裸,对点权排序,然后双指针扫扫,接下来就是链接两棵子树或者拆分两棵子树时的子树的siz。子树siz可以用两个变量维护,一个维护splay树内的子树的siz和,一个维护从这个点连出去的不在splay树上的子树的siz和。然后就做完了。
点分治一般做法复杂度 O(Nlog2N) ,听说神奇的单调队列做法可以达到 O(NlogN) ,动态树复杂度 O(NlogN)
PS:比赛的时候,没从子树去重类入手,用枚举子树的方法思考怎么都不能去除重复的计数,于是没想到点分治的方法怎么做。而我想到用动态树的瞬间就知道怎么用动态树做了,但是敲到一半的时候竟然傻逼的找了个奇怪的理由否决了= =也是傻逼……导致比赛的时候没做出来……
my code:
#include <stdio.h>
#include <string.h>
#include <bitset>
#include <algorithm>
using namespace std ;
typedef long long LL ;
#define clr( a , x ) memset ( a , x , sizeof a )
const int MAXN = 100005 ;
const int MAXM = 10000005 ;
const int MAXE = 200005 ;
const int INF = 0x3f3f3f3f ;
struct Edge {
int v , n ;
Edge () {}
Edge ( int v , int n ) : v ( v ) , n ( n ) {}
} ;
struct Point {
int x , idx ;
bool operator < ( const Point& a ) const {
return x < a.x ;
}
} ;
LL ans ;
struct Node* null ;
struct Node {
Node* c[2] ;
Node* f ;
int siz , outsiz ;
int flip ;
void newnode () {
c[0] = c[1] = f = null ;
flip = 0 ;
siz = 1 ;
outsiz = 0 ;
}
void reverse () {
if ( this == null ) return ;
swap ( c[0] , c[1] ) ;
flip ^= 1 ;
}
void up () {
if ( this == null ) return ;
siz = c[0]->siz + c[1]->siz + c[0]->outsiz + c[1]->outsiz + 1 ;
}
void down () {
if ( this == null ) return ;
if ( flip ) {
c[0]->reverse () ;
c[1]->reverse () ;
flip = 0 ;
}
}
bool is_root () {
return f == null || f->c[0] != this && f->c[1] != this ;
}
void setc ( Node* o , int d ) {
c[d] = o ;
o->f = this ;
}
void sign_down () {
if ( !is_root () ) f->sign_down () ;
down () ;
}
void rot ( int d ) {
Node* p = f ;
Node* g = p->f ;
p->setc ( c[d] , !d ) ;
if ( !p->is_root () ) g->setc ( this , f == g->c[1] ) ;
else f = g ;
setc ( p , d ) ;
p->up () ;
}
Node* splay () {
sign_down () ;
while ( !is_root () ) {
if ( f->is_root () ) rot ( this == f->c[0] ) ;
else {
if ( f == f->f->c[0] ) {
if ( this == f->c[0] ) f->rot ( 1 ) , rot ( 1 ) ;
else rot ( 0 ) , rot ( 1 ) ;
} else {
if ( this == f->c[1] ) f->rot ( 0 ) , rot ( 0 ) ;
else rot ( 1 ) , rot ( 0 ) ;
}
}
}
up () ;
return this ;
}
Node* access () {
Node* o = this ;
Node* x = null ;
while ( o != null ) {
if ( x != null ) {
while ( x->c[0] != null ) x = x->c[0] ;
x->splay () ;
}
o->splay () ;
o->outsiz += o->c[1]->outsiz + o->c[1]->siz ;
o->outsiz -= x->outsiz + x->siz ;
o->setc ( x , 1 ) ;
o->up () ;
x = o ;
o = o->f ;
}
return splay () ;
}
void make_root () {
access ()->reverse () ;
splay () ;
}
void link ( Node* o ) {
make_root () ;
o->make_root () ;
ans += ( LL ) ( siz + outsiz ) * ( o->siz + o->outsiz ) ;
o->outsiz += siz + outsiz ;
f = o ;
}
void cut () {
access () ;
c[0]->f = null ;
c[0] = null ;
up () ;
}
void cut ( Node* o ) {
make_root () ;
o->cut () ;
}
} ;
Node pool[MAXN] ;
Node* cur ;
Node* node[MAXN] ;
Edge E[MAXE] ;
int H[MAXN] , cntE ;
Point p[MAXN] ;
int is[MAXN] ;
int n , D ;
void clear () {
cur = pool ;
cur->newnode () ;
null = cur ++ ;
null->siz = 0 ;
}
void init () {
cntE = 0 ;
clr ( is , 0 ) ;
clr ( H , -1 ) ;
}
void addedge ( int u , int v ) {
E[cntE] = Edge ( v , H[u] ) ;
H[u] = cntE ++ ;
}
void scanf ( int& x , char c = 0 ) {
while ( ( c = getchar () ) < '0' ) ;
x = c - '0' ;
while ( ( c = getchar () ) >= '0' ) x = x * 10 + c - '0' ;
}
void solve () {
int u , v ;
init () ;
clear () ;
scanf ( n ) ;
scanf ( D ) ;
for ( int i = 1 ; i <= n ; ++ i ) {
scanf ( p[i].x ) ;
p[i].idx = i ;
cur->newnode () ;
node[i] = cur ++ ;
}
sort ( p + 1 , p + n + 1 ) ;
for ( int i = 1 ; i < n ; ++ i ) {
scanf ( u ) ;
scanf ( v ) ;
addedge ( u , v ) ;
addedge ( v , u ) ;
}
int l = 1 ;
ans = 0 ;
for ( int i = 1 ; i <= n ; ++ i ) {
while ( l <= i && p[i].x - p[l].x > D ) {
int u = p[l].idx ;
for ( int j = H[u] ; ~j ; j = E[j].n ) {
int v = E[j].v ;
if ( !is[v] ) continue ;
node[u]->cut ( node[v] ) ;
}
is[u] = 0 ;
++ l ;
}
int u = p[i].idx ;
for ( int j = H[u] ; ~j ; j = E[j].n ) {
int v = E[j].v ;
if ( !is[v] ) continue ;
node[u]->link ( node[v] ) ;
}
is[u] = 1 ;
}
printf ( "%lld\n" , ans * 2 ) ;
}
int main () {
int T ;
scanf ( T ) ;
for ( int i = 1 ; i <= T ; ++ i ) {
solve () ;
}
return 0 ;
}