Successor
Time Limit : 2000/1000ms (Java/Other) Memory Limit : 65536/65536K (Java/Other)
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
//题意:总共有n个人(0—n-1),其中编号为0的是老板,1—n-1是员工,每个员工有3个参数,他上司的编号,忠诚度和能力值,每个人只有1个上司,每个人的忠诚度唯一,且每个人的编号肯定比他的上司大。现在老板要开除员工,并且要在这个被开除的人的下属或他下属的下属里去找一个人,要求能力值比这个被开除的人大(不能等于),并且忠诚度要在满足以上要求的人中最大,来顶替那个被开除的人。
PS:如果找到了这个人输出后,不用真的在线段树中把这个人去跟那个被开除的人交换,题目意思是如果这个人被开除了谁去顶替,是如果!...
//思路:
1.首先,要确定所有员工之间的上下级关系,我们先用c++里的vector容器来存每个人的下属(这里的
vector容器其实就是一个二维数组,但不能用二维数组,因为他大小要map[50000][50000],会爆栈的...),然后用一个DFS,记录递归的时候每个人DFS进去的时间和DFS结束的时间,这样每个人就有了一个区间,而且一个人的下属和他下属的下属的区间必然是他区间的一个子集。
2.第一步完成之后,我们先建线段树,线段树里一个节点包含2个值id和max_loyality,表示这个线段树这个节点区间里忠诚度的最大值和那个人的节点id,一开始建树的时候把这些都先设置成-1好了。
3.再把所有员工按能力值从大到小排个序,然后依次去update线段树,这题线段树是单点更新的,更新的时候节点的位置是第一步DFS确定的区间的左端点,即进DFS的时间,因为这个时间每个人必然是不同的,根据这个去确定这个人在线段树中的位置。然后向上Pushup的时候,父亲节点的值是左右孩子中忠诚度的最大值和那个节点的id。
4.因为我们是按能力值从大到小去update的,我们只要在插入那个点之前去寻找那个线段树中满足要求替换他的人就行了,因为替换者的能力值是一定要比他大的。查询的时候,要输入要查的那个人DFS确定的区间(来确定是不是被删的那个人的下属或下属的下属)。query求出的是满足要求的最大忠诚度,因为题目说每个人的忠诚度是唯一的,即一个忠诚度只对应1个人,我们可以通过一个hash[],用忠诚度来映射这个人的id,注意忠诚度最大是有
1000000的,所以hash数组也要开这么大。
具体实现看代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <algorithm>
using namespace std;
const int MAX = 50000 + 100;
const int MAXN = 1000000 + 100;
typedef struct {
int id; //id
int superior; //上司id
int loyalty; //忠诚度
int ability; //能力值
}Staff;
typedef struct {
int loy; //最大的忠诚度的值
int id; //忠诚度最大的这个人的id
}Tree;
typedef struct {
int s, e;//分别表示dfs进去的时间和出来的时间
}Node;
Tree tree[MAX * 4]; //线段树数组
Staff staff[MAX]; //员工的信息
Node area[MAX]; //每个员工dfs的区间
vector<int> edge[MAX]; //储存每个人他所有下属的id
int ans[MAX]; //用来储存如果炒掉这个人之后来顶替他的那个人的id
int ha[MAXN]; //通过忠诚度来映射一个人的id(因为每个人忠诚度唯一)
int dfstime;
bool cmp(Staff a, Staff b)
{
//按能力值从大到小排序
return a.ability > b.ability;
}
void dfs(int x)
{
area[x].s = dfstime++;
for (int i = 0; i < edge[x].size(); i++)
{
dfs(edge[x][i]);
}
area[x].e = dfstime;
}
void Pushup(int root)
{
//两个节点的父亲节点是他们中忠诚度最大的那个
if (tree[root << 1].loy > tree[root << 1 | 1].loy)
{
tree[root].loy = tree[root << 1].loy;
tree[root].id = tree[root << 1].id;
}
else
{
tree[root].loy = tree[root << 1 | 1].loy;
tree[root].id = tree[root << 1 | 1].id;
}
}
void build(int root, int l, int r)
{
//一开始都先设置成-1
//后面会逐个单点更新
tree[root].id = tree[root].loy = -1;
if (l == r)
return;
int mid = (l + r) / 2;
build(root << 1, l, mid);
build(root << 1 | 1, mid + 1, r);
}
//线段树中我们用dfs进去的那个时间来确定节点在线段树的位置
//因为dfs进去的那个时间必然唯一
//单点更新
void update(int root, int l, int r, int pos, int id, int val)
{
if (l == r)
{
tree[root].id = id;
tree[root].loy = val;
return;
}
int mid = (l + r) / 2;
if (pos <= mid)
update(root << 1, l, mid, pos, id, val);
else
update(root << 1 | 1, mid + 1, r, pos, id, val);
Pushup(root);
}
//找符合条件的最大忠诚度
int query(int root, int l, int r, int L, int R)
{
if (L > R)
return -1;
if (L <= l&&r <= R)
return tree[root].loy;
int mid = (l + r) / 2;
int ret = -1;
if (L <= mid)
ret = max(ret, query(root << 1, l, mid, L, R));
if (R > mid)
ret = max(ret, query(root << 1 | 1, mid + 1, r, L, R));
return ret;
}
int main()
{
int T;
int n, m;
scanf("%d", &T);
while (T--)
{
int a;
scanf("%d%d", &n, &m);
memset(ha, 0, sizeof(ha));
for (int i = 0; i <= n; i++)
edge[i].clear();
for (int i = 1; i <= n - 1; i++)
{
scanf("%d%d%d", &staff[i].superior, &staff[i].loyalty, &staff[i].ability);
edge[staff[i].superior].push_back(i);
staff[i].id = i;
ha[staff[i].loyalty] = i;
}
sort(staff + 1, staff + n, cmp);
dfstime = 0;
dfs(0);
build(1, 0, dfstime - 1);
memset(ans, -1, sizeof(ans));
/*
按能力从大到小去插入,这样每次查询查的人都是能力比他大的,
这样我们只要去找符合条件的最大忠诚度就可以了。
怎么样算符合条件:
是这个人的下属或者下属的下属,只要满足去替换的那个人的dfs
进出区间包含在这个人区间里就行了。
*/
for (int i = 1, j; i < n; i = j)
{
j = i;
//逐个查询能力值相同的人
//因为顶替他的人的能力一定要比他强,不能相同
while (j < n&&staff[j].ability == staff[i].ability)
{
int id = staff[j].id;
int loy = query(1, 0, dfstime - 1, area[id].s + 1, area[id].e - 1);
ans[id] = (loy == -1 ? -1 : ha[loy]);
j++;
}
j = i;
//逐个update能力值相同的人的信息
while (j < n&&staff[j].ability == staff[i].ability)
{
int id = staff[j].id;
update(1, 0, dfstime - 1, area[id].s, id, staff[j].loyalty);
j++;
}
}
for (int i = 0; i < m; i++)
{
scanf("%d", &a);
printf("%d\n", ans[a]);
}
}
return 0;
}