1 3 2 0 100 99 1 101 100 1 2
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 ;
}