【HDU】5111 Alexandra and Two Trees 树链剖分+主席树【树上路径的交集】【在线算法】

传送门:【HDU】5111 Alexandra and Two Trees


题目分析:

我们首先考虑线段上的此问题。

我们有两个序列,序列1,序列2。首先我们将序列2的权值映射成序列1中和该权值相同的位置的下标(正是因为要保证映射唯一,所以序列1内的数要各不相同,序列2不需要各不相同),如果数不存在就映射到编号0。我们需要用到主席树——可持久化线段树,按照序列2从左到右的顺序依次插入每个权值到主席树中。然后每次查询【L1,R1】、【L2,R2】,答案就是第R2棵主席树中权值在【L1,R1】内的个数(R2中小于等于R1的和 - R2中小于等于L1-1的和)减去第L2-1棵主席树中权值在【L1,R1】内的个数(同上)。


然后将问题转化到树上。为了保证算法的复杂度,我们考虑使用树链剖分,将树分成logN段连续的线段,这样【u1,v1】上最多包括logN条连续的线段,【u2,v2】上最多包括logN条连续的线段。这样每次询问转化成【u1,v1】上每条线段和【u2,v2】上每条线段交集的和!


算法复杂度O(N*log^3N)。


一开始连线段上的交集都不会求,但是在我们强大的大一双金爷zhou神的指导下会了线段上的交集的求法,然后树上的算法就很容易想到了。


代码如下:


#include <map>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

typedef long long LL ;

#pragma comment(linker, "/STACK:16777216")
#define rep( i , a , b ) for ( int i = ( a ) ; i <  ( b ) ; ++ i )
#define For( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i )
#define rev( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i )
#define clr( a , x ) memset ( a , x , sizeof a )
#define mid ( ( l + r ) >> 1 )

const int MAXN = 100005 ;
const int MAXE = 100005 ;

struct Node {
	Node* c[2] ;
	int sum ;
} ;

struct Edge {
	int v , n ;
	Edge () {}
	Edge ( int v , int n ) : v ( v ) , n ( n ) {}
} ;

struct Seg {
	int L , R ;
	Seg () {}
	Seg ( int L , int R ) : L ( L ) , R ( R ) {}
} ;

struct HeavyLightDecompose {
	Edge E[MAXE] ;
	int H[MAXN] , cntE ;
	int pre[MAXN] ;
	int pos[MAXN] ;
	int dep[MAXN] ;
	int siz[MAXN] ;
	int son[MAXN] ;
	int top[MAXN] ;
	int val[MAXN] ;
	int tree_idx ;

	void clear () {
		tree_idx = 0 ;
		dep[1] = 0 ;
		pre[1] = 0 ;
		siz[0] = 0 ;
		cntE = 0 ;
		clr ( H , -1 ) ;
	}

	void addedge ( int u , int v ) {
		E[cntE] = Edge ( v , H[u] ) ;
		H[u] = cntE ++ ;
	}

	void dfs ( int u ) {
		siz[u] = 1 ;
		son[u] = 0 ;
		for ( int i = H[u] ; ~i ; i = E[i].n ) {
			int v = E[i].v ;
			if ( v == pre[u] ) continue ;
			pre[v] = u ;
			dep[v] = dep[u] + 1 ;
			dfs ( v ) ;
			siz[u] += siz[v] ;
			if ( siz[v] > siz[son[u]] ) son[u] = v ;
		}
	}

	void rebuild ( int u , int top_element ) {
		top[u] = top_element ;
		pos[u] = ++ tree_idx ;
		if ( son[u] ) rebuild ( son[u] , top_element ) ;
		for ( int i = H[u] ; ~i ; i = E[i].n ) {
			int v = E[i].v ;
			if ( v != pre[u] && v != son[u] ) {
				rebuild ( v , v ) ;
			}
		}
	}
} ;

Node pool[MAXN * 50] ;
Node* root[MAXN] ;
Node* cur ;
HeavyLightDecompose T1 , T2 ;
Seg seg[MAXN] ;
int top ;
int cnt ;
int n1 , n2 , q ;
map < int , int > mp ;

void clear () {
	T1.clear () ;
	T2.clear () ;
	mp.clear () ;
	cur = pool ;
}

void build ( Node* &now , int l , int r ) {
	now = cur ++ ;
	now->sum = 0 ;
	if ( l == r ) return ;
	int m = mid ;
	build ( now->c[0] , l , m ) ;
	build ( now->c[1] , m + 1 , r ) ;
}

void insert ( Node* &now , Node* old , int x , int v , int l , int r ) {
	now = cur ++ ;
	if ( l == r ) {
		now->sum = old->sum + v ;
		return ;
	}
	int m = mid ;
	if ( x <= m ) {
		now->c[1] = old->c[1] ;
		insert ( now->c[0] , old->c[0] , x , v , l , m ) ;
	} else {
		now->c[0] = old->c[0] ;
		insert ( now->c[1] , old->c[1] , x , v , m + 1 , r ) ;
	}
	now->sum = now->c[0]->sum + now->c[1]->sum ;
}

int query ( Node* now , Node* old , int x , int l , int r ) {
	if ( x == 0 ) return 0 ;
	int ans = 0 ;
	while ( l < r ) {
		int m = mid ;
		if ( x <= m ) {
			now = now->c[0] ;
			old = old->c[0] ;
			r = m ;
		} else {
			//printf ( "%d %d %d %d\n" , now->c[0]->sum , old->c[0]->sum , l , r ) ;
			ans += now->c[0]->sum - old->c[0]->sum ;
			now = now->c[1] ;
			old = old->c[1] ;
			l = m + 1 ;
		}
	}
	//printf ( "-------%d %d\n" , x , ans ) ;
	ans += now->sum - old->sum ;
	//printf ( "-------%d %d\n" , x , ans ) ;
	return ans ;
}

void dfs ( int u ) {
	if ( mp.count ( T2.val[u] ) ) insert ( root[T2.pos[u]] , root[T2.pos[u] - 1] , mp[T2.val[u]] , 1 , 1 , cnt ) ;
	else root[T2.pos[u]] = root[T2.pos[u] - 1] ;
	//if ( mp.count ( T2.val[u] ) ) printf ( "%d %d\n" , u , mp[T2.val[u]] ) ;
	//else printf ( "%d %d\n" , u , 0 ) ;
	if ( T2.son[u] ) dfs ( T2.son[u] ) ;
	for ( int i = T2.H[u] ; ~i ; i = T2.E[i].n ) {
		int v = T2.E[i].v ;
		if ( v != T2.pre[u] && v != T2.son[u] ) {
			dfs ( v ) ;
		}
	}
}

void get_seg ( int x , int y ) {
	top = 0 ;
	while ( T1.pos[T1.top[x]] != T1.pos[T1.top[y]] ) {
		if ( T1.dep[T1.top[x]] < T1.dep[T1.top[y]] ) swap ( x , y ) ;
		seg[top ++] = Seg ( T1.pos[T1.top[x]] , T1.pos[x] ) ;
		x = T1.pre[T1.top[x]] ;
	}
	if ( T1.dep[x] > T1.dep[y] ) swap ( x , y ) ;
	seg[top ++] = Seg ( T1.pos[x] , T1.pos[y] ) ;
}

int get_sum ( int x , int y ) {
	int ans = 0 ;
	while ( T2.pos[T2.top[x]] != T2.pos[T2.top[y]] ) {
		if ( T2.dep[T2.top[x]] < T2.dep[T2.top[y]] ) swap ( x , y ) ;
		rep ( i , 0 , top ) {
			int L = seg[i].L ;
			int R = seg[i].R ;
			ans -= query ( root[T2.pos[x]] , root[T2.pos[T2.top[x]] - 1] , L - 1 , 1 , cnt ) ;
			ans += query ( root[T2.pos[x]] , root[T2.pos[T2.top[x]] - 1] , R , 1 , cnt ) ;
		}
		x = T2.pre[T2.top[x]] ;
	}
	if ( T2.dep[x] > T2.dep[y] ) swap ( x , y ) ;
	rep ( i , 0 , top ) {
		int L = seg[i].L ;
		int R = seg[i].R ;
		ans -= query ( root[T2.pos[y]] , root[T2.pos[x] - 1] , L - 1 , 1 , cnt ) ;
		ans += query ( root[T2.pos[y]] , root[T2.pos[x] - 1] , R , 1 , cnt ) ;
	}
	return ans ;
}

void solve () {
	int u , v ;
	clear () ;
	cnt = n1 ;
	For ( i , 2 , n1 ) {
		scanf ( "%d" , &u ) ;
		T1.addedge ( u , i ) ;
	}
	For ( i , 1 , n1 ) scanf ( "%d" , &T1.val[i] ) ;
	scanf ( "%d" , &n2 ) ;
	For ( i , 2 , n2 ) {
		scanf ( "%d" , &u ) ;
		T2.addedge ( u , i ) ;
	}
	For ( i , 1 , n2 ) scanf ( "%d" , &T2.val[i] ) ;
	//cnt = unique ( n1 ) ;
	
	T1.dfs ( 1 ) ;
	T1.rebuild ( 1 , 1 ) ;
	For ( i , 1 , n1 ) mp[T1.val[i]] = T1.pos[i] ;
	//For ( i , 1 , n1 ) printf ( "%d %d\n" , i , T1.pos[i] ) ;
	build ( root[0] , 1 , cnt ) ;
	T2.dfs ( 1 ) ;
	T2.rebuild ( 1 , 1 ) ;
	dfs ( 1 ) ;
	scanf ( "%d" , &q ) ;
	while ( q -- ) {
		scanf ( "%d%d" , &u , &v ) ;
		get_seg ( u , v ) ;
		scanf ( "%d%d" , &u , &v ) ;
		//rep ( i , 0 , top ) printf ( "%d %d\n" , seg[i].L , seg[i].R ) ;
		int ans = get_sum ( u , v ) ;
		printf ( "%d\n" , ans ) ;
	}
}

int main () {
	while ( ~scanf ( "%d" , &n1 ) ) solve () ;
	return 0 ;
}


------------------------------update------------------------------

根本不用树链剖分的。。。是我逗比。。主席树只要按照dfs序插入,然后路径就是u,v减去两倍的lca就好。

所以复杂度为O(Nlog^2N)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值