HDU 4366 Successor 线段树

Successor

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 2173    Accepted Submission(s): 506


Problem Description
Sean owns a company and he is the BOSS.The other Staff has one Superior.every staff has a loyalty and ability.Some times Sean will fire one staff.Then one of the fired man’s Subordinates will replace him whose ability is higher than him and has the highest loyalty for company.Sean want to know who will replace the fired man.
 

Input
In the first line a number T indicate the number of test cases. Then for each case the first line contain 2 numbers n,m (2<=n,m<=50000),indicate the company has n person include Sean ,m is the times of Sean’s query.Staffs are numbered from 1 to n-1,Sean’s number is 0.Follow n-1 lines,the i-th(1<=i<=n-1) line contains 3 integers a,b,c(0<=a<=n-1,0<=b,c<=1000000),indicate the i-th staff’s superior Serial number,i-th staff’s loyalty and ability.Every staff ‘s Serial number is bigger than his superior,Each staff has different loyalty.then follows m lines of queries.Each line only a number indicate the Serial number of whom should be fired.
 

Output
For every query print a number:the Serial number of whom would replace the losing job man,If there has no one to replace him,print -1.
 

Sample Input
  
  
1 3 2 0 100 99 1 101 100 1 2
 

Sample Output
  
  
2 -1
 

Author
FZU
 

Source

Source
HDOJ Monthly Contest – 2010.05.01

传送门:HDU 4366 Successor

题目大意:你的公司包括你一共有n个人,其中你是BOSS,编号为0,其余人的编号为1~n-1。除你以外每个人有一个上司,能力值以及声望值。现在你做出m次假设,每次你要炒掉一个人x的鱿鱼,然后在他的下级(包括下级的下级以及下级的下级的下级……)中选出一个人,满足这个下级的能力比x要高并且是所有比x高的下级之中声望最高的(不高别人才不让嘞)。如果存在,输出这个人的编号,否则,输出-1。

题目分析:根据题目信息,我们知道这是一个树形图,且0是根结点,因此根据这一层信息,我们可以做一次DFS,
in[ i ]表示进入DFS的时间,ou[ i ]表示离开DFS的时间,得到每个人所属的DFS区间[ in[ i ] , ou[ i ] ]。DFS区间的特点就是:所有属于x的下级的区间都完全包含于x所表示的区间之内。
假设我们已知该怎么更新信息,接下来,我们直接按照1~n-1的顺序插入每个人,由于in[ i ] 和ou[ i ]表示一个人,所以我们选择一个插入信息即可。在插入以后逐层往上更新信息。
怎么样保存信息才能满足要求呢?
首先看查询部分。
对于每个节点,我们保存两个值:以这个节点为根结点的子树中声望值最大的人的编号,以及子树中最大的能力值。
这样查询信息的时候,如果进入一个完全包含于被炒鱿鱼的员工x的DFS区间内的区间,将有以下三种情况:
1.如果该节点保存的最大能力值不大于x,无解,直接返回0(设编号为0表示的人的能力值以及声望都是最小的)。
2.如果该节点保存的声望值最大的人的能力值同样比x高,那么直接返回这个人的编号。
3.如果不满足1、2但是子区间中存在可行解(即子区间中保存的能力值大于x的能力值),继续向下搜索,然后返回可行解中的声望值最大的人的编号。
既然查询已经OK了,那么更新自然很容易知道该怎么做了:更新时,父节点保存子节点中声望值最大的人的编号,以及子区间中最大的能力值即可。

线段树直接插插插……最后一直查询就好了。
PS:第一次跑了倒数。。。怎么说呢,不说了,上代码QUQ

代码如下:

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

#define lson l , m , o << 1
#define rson m + 1 , r , o << 1 | 1
#define clear( A , X ) memset ( A , X , sizeof A )

const int maxN = 500000 ;
const int maxM = 50005 ;

struct Node {
	int se , st ;
} ;

struct Edge {
	int v , n ;
} ;


Node a[maxM] ;
Edge edge[maxN] ;
int adj[maxM] , cntE ;
int in[maxM] , ou[maxM] ;
int dfs_clock ;
int number[maxN] , mmax[maxN] ;

int max ( const int X , const int Y ) {
	if ( X > Y ) return X ; 
	return Y ;
}

void addedge ( int u , int v ) {
	edge[cntE].v = v ; edge[cntE].n = adj[u] ; adj[u] = cntE ++ ;
}

void DFS ( int u ) {
	in[u] = ++ dfs_clock ;
	for ( int i = adj[u] ; ~i ; i = edge[i].n ) DFS ( edge[i].v ) ;
	ou[u] = ++ dfs_clock ;
}

void PushUp ( int o ) {
	int ll = number[o << 1] , rr = number[o << 1 | 1] ;
	number[o] = ( a[ll].st > a[rr].st ? ll : rr ) ;
	mmax[o] = max ( mmax[o << 1] , mmax[o << 1 | 1] ) ;
}

void Update ( int x , int i , int l , int r , int o ) {
	if ( l == x && x == r ) {
		number[o] = i ;
		mmax[o] = a[i].se ;
		return ;
	}
	int m = ( l + r ) >> 1 ;
	if ( x <= m ) Update ( x , i , lson ) ;
	else Update ( x , i , rson ) ;
	PushUp ( o ) ;
}

int Query ( int L , int R , int x , int l , int r , int o ) {
	if ( mmax[o] <= x ) return 0 ;
	if ( L <= l && r <= R ) {
		//printf ( "%d = %d\n" , number[o] , a[number[o]].se ) ;
		if ( a[number[o]].se > x ) {
			return number[o] ;
		}
	}
	int m = ( l + r ) >> 1 , ans1 = 0 , ans2 = 0 ;
	if ( L <= m && mmax[o << 1] > x ) ans1 = Query ( L , R , x , lson ) ;
	if ( m <  R && mmax[o << 1 | 1] > x ) ans2 = Query ( L , R , x , rson ) ;
	if ( a[ans1].st > a[ans2].st ) return ans1 ;
	return ans2 ;
}

void work () {
	int n , m , u , x ;
	scanf ( "%d%d" , &n , &m ) ;
	a[0].st = a[0].se = 0 ;
	clear ( adj , -1 ) ;
	clear ( mmax , 0 ) ;
	clear ( number , 0 ) ;
	cntE = 0 ;
	dfs_clock = 0 ;
	for ( int i = 1 ; i < n ; ++ i) {
		scanf ( "%d%d%d" , &u , &a[i].st , &a[i].se ) ;
		addedge ( u , i ) ;
	}
	DFS ( 0 ) ;
	for ( int i = 1 ; i < n ; ++ i ) {
		Update ( in[i] , i , 1 , dfs_clock , 1 ) ;
	}
	while ( m -- ) {
		scanf ( "%d" , &x ) ;
		if ( in[x] + 1 == ou[x] ) {
			printf ( "-1\n" ) ;
			continue ;
		}
		int ans = Query ( in[x] + 1 , ou[x] - 1 , a[x].se , 1 , dfs_clock , 1 ) ;
		if ( !ans ) printf ( "-1\n" ) ;
		else printf ( "%d\n" , ans ) ;
	}
}

int main () {
	int T ;
	scanf ( "%d" , &T ) ;
	while ( T -- ) work () ;
	return 0 ;
}


下面是后来写的代码,用排序做了预处理,这样边插边记录结果,最后直接O(1)输出就好了。
方法:按照能力值从大到小排序,对于每个员工,先查询一次,然后插入该员工。易知每次查询时,所有能够查到的下级必定都是能力比他高的。这样效率能优化很多。

代码如下:

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

#define lson l , m , o << 1
#define rson m + 1 , r , o << 1 | 1
#define clear( A , X ) memset ( A , X , sizeof A )

const int maxN = 500000 ;
const int maxM = 50005 ;

struct Node {
	int se , st , i , in , ou ;
	bool operator < ( const Node &t ) const {
		return se > t.se ;
	}
} ;

struct Edge {
	int v , n ;
} ;


Node a[maxM] ;
Edge edge[maxN] ;
int adj[maxM] , cntE ;
int dfs_clock ;
int number[maxN] , mmax[maxN] ;
int ask[maxN] ;

void addedge ( int u , int v ) {
	edge[cntE].v = v ; edge[cntE].n = adj[u] ; adj[u] = cntE ++ ;
}

void DFS ( int u ) {
	a[u].in = ++ dfs_clock ;
	for ( int i = adj[u] ; ~i ; i = edge[i].n ) DFS ( edge[i].v ) ;
	a[u].ou = ++ dfs_clock ;
}

void PushUp ( int o ) {
	int ll = number[o << 1] , rr = number[o << 1 | 1] ;
	number[o] = ( a[ll].st > a[rr].st ? ll : rr ) ;
	mmax[o] = ( mmax[o << 1] > mmax[o << 1 | 1] ? mmax[o << 1] : mmax[o << 1 | 1] ) ;
}

void Update ( int x , int i , int l , int r , int o ) {
	if ( l == r ) {
		number[o] = i ;
		mmax[o] = a[i].se ;
		return ;
	}
	int m = ( l + r ) >> 1 ;
	if ( x <= m ) Update ( x , i , lson ) ;
	else Update ( x , i , rson ) ;
	PushUp ( o ) ;
}

int Query ( int L , int R , int x , int l , int r , int o ) {
	if ( mmax[o] == x ) return 0 ;
	if ( L <= l && r <= R ) {
		if ( a[number[o]].se > x ) return number[o] ;
	}
	int m = ( l + r ) >> 1 , ans1 = 0 , ans2 = 0 ;
	if ( L <= m && mmax[o << 1] > x ) ans1 = Query ( L , R , x , lson ) ;
	if ( m <  R && mmax[o << 1 | 1] > x ) ans2 = Query ( L , R , x , rson ) ;
	return a[ans1].st > a[ans2].st ? ans1 : ans2 ;
}

void work () {
	int n , m , u , x ;
	scanf ( "%d%d" , &n , &m ) ;
	a[0].st = a[0].se = 0 ;
	a[0].i = -1 ;
	clear ( adj , -1 ) ;
	clear ( mmax , 0 ) ;
	clear ( number , 0 ) ;
	cntE = 0 ;
	dfs_clock = 0 ;
	for ( int i = 1 ; i < n ; ++ i) {
		scanf ( "%d%d%d" , &u , &a[i].st , &a[i].se ) ;
		a[i].i = i ;
		addedge ( u , i ) ;
	}
	DFS ( 0 ) ;
	sort ( a + 1 , a + n ) ;
	for ( int i = 1 ; i < n ; ++ i ) {
		if ( a[i].in + 1 == a[i].ou ) ask[a[i].i] = -1 ;
		else {
			int ans = Query ( a[i].in + 1 , a[i].ou - 1 , a[i].se , 1 , dfs_clock , 1 ) ;
			ask[a[i].i] = a[ans].i ;
		}
		Update ( a[i].in , i , 1 , dfs_clock , 1 ) ;
	}
	while ( m -- ) {
		scanf ( "%d" , &x ) ;
		printf ( "%d\n" , ask[x] ) ;
	}
}

int main () {
	int T ;
	scanf ( "%d" , &T ) ;
	while ( T -- ) work () ;
	return 0 ;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值