题目链接
题意
公司里的每个员工都有一个忠诚度和能力值。
如果把一个员工开除,需要在他的下属中,找到一个能力值比他高,且忠诚度最大的员工来替代他。
第一行一个整数,表示测试数据组数。
对于每组数据,第一行两个整数n和m,表示公司的员工数和查询数。
接下来n-1行,第i行,3个整数,i的上级,忠诚度,能力值。老总的编号为0,所有员工的忠诚度都不一样。
接下来m行查询,每行一个整数,表示要开除某个人,输出替代他的员工编号。如果无法替代,输出-1.
思路
用DFS序转为线性存储,每个人的下级在俩时间戳内。
将每个人按能力值排序。用线段树维护单点修改,区间查询的忠诚度最大值。
按能力从大到小,每次先查询此人的时间戳内忠诚度最大值,如果不存在即-1,存在即表示可被替代,然后单点修改。
由于每个人忠诚度不一样,可以用map来维护忠诚度与人的编号关系。
有个问题,如果俩人能力值相同,如果先更新儿子节点,那么查询会查到父亲的值。
所以要先更新父亲节点,排序时使能力值相同的人时间戳小的排前面即可。
代码
#include<bits/stdc++.h>
using namespace std;
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
vector<int> e[50005];
map<int, int> mp;
int tot, ma[200005], ans[50005];
struct Node
{
int lo, ab, l, r;
}data[50005];
bool cmp(Node a, Node b)
{
if(a.ab != b.ab) return a.ab > b.ab;
return a.l < b.l;
}
void init(int n)
{
tot = 0;
mp.clear();
mp[-1] = -1;
for(int i = 0; i <= n; ++i) e[i].clear();
memset(ma,-1,sizeof(ma));
}
void dfs(int u)
{
data[u].l = ++tot;
for(auto v : e[u]) dfs(v);
data[u].r = tot;
}
void updata(int rt, int l, int r, int ql, int lo)
{
if(l == r)
{
ma[rt] = lo;
return;
}
int mid = (l+r)>>1;
if(ql <= mid) updata(lson,ql,lo);
else updata(rson,ql,lo);
ma[rt] = max(ma[rt<<1], ma[rt<<1|1]);
}
int query(int rt, int l, int r, int ql, int qr)
{
if(ql <= l && r <= qr) return ma[rt];
int mid = (l+r)>>1, tmp = -1;
if(ql <= mid) tmp = max(tmp, query(lson,ql,qr));
if(mid < qr) tmp = max(tmp, query(rson,ql,qr));
return tmp;
}
int main()
{
int t;
for(scanf("%d",&t); t; --t)
{
int n, m, u;
scanf("%d%d",&n,&m);
init(n);
for(int i = 1; i < n; ++i)
{
scanf("%d%d%d",&u,&data[i].lo,&data[i].ab);
mp[data[i].lo] = i;
e[u].push_back(i);
}
dfs(0);
sort(data+1,data+n,cmp);
for(int i = 1; i < n; ++i)
{
ans[mp[data[i].lo]] = mp[query(1,1,tot,data[i].l,data[i].r)];
updata(1,1,tot,data[i].l,data[i].lo);
}
for(; m--; printf("%d\n",ans[u])) scanf("%d",&u);
}
return 0;
}