【FJ 2013】继任者

Description

S开了家公司,他自己是老板,其余员工都有一名直系上级,每名员工都有对公司的忠诚度和能力值。S时不时会开除某名员工,由其下属中能力值大于这名员工的人中忠诚度最高的担任其职务。他想知道,若开除某名员工,该选谁担任其职务。A是B的下属是指,A的直系上级是B或B的下属。

Input

第一行输入T表示CASE数。接下来T组数据,每组数据第一行输入两个整数n,m(2<=n,m<=25000),表示有该公司包括S有n个人,有m组询问。员工按输入顺序从1到n-1编号,S编号为0。接下去1到n-1行,每行三个整数a,b,c(0<=a<=n-1,0<=b,c<=1000000)分别表示第i名员工的直系上级编号、忠诚度、能力值,每名员工的编号大于其上级的编号,且他们的忠诚度各不相同。接下去m行,每行一个整数q(1<=q<=n-1)表示询问若开除第q名员工应该选谁担任其职务。注意询问并没有真正开除员工,也就是说各个询问假设要开除的员工互不影响。

Output

对于每个询问输出一个数表示该选的员工的编号,若这样的员工不存在则输出-1。

SampleInput

3 2
0 100 99
1 101 100
1
2

SampleOutput

2
-1

Solution

人员结构事实上是树形关系,一个人的继任者必然在他的子树内。
这类问题显然可以通过dfs序+线段树解决。

那么要如何处理能力值大于这名员工的人中忠诚度最高的问题呢?
不妨建一颗空的线段树,维护人员忠诚度的最大值。然后把人员按照能力值从大到小排序。
在将每个人插入线段树之前先查询他子树区间内忠诚度最大的人员即可。这样就能确保线段树内人员的能力值一定比当前人员的能力值大。

时间复杂度为O(n+nlogn)

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

const int N=25002;

int n,m,pn,head[N],cnt,fd[N],ans[N],mx[N],ro[N];

struct worker{int ab,ro,dfn,l,r,id;}p[N];

struct edge{int to,next;}e[N];

struct seg_tree{int mx,id,l,r;}tree[4*N];

int getint(){
    int v=0;char ch;
    while(!isdigit(ch=getchar())); v=ch-48;
    while(isdigit(ch=getchar())) v=v*10+ch-48; return v;
}

bool cmp(worker a,worker b){return(a.ab>b.ab || a.ab==b.ab && a.dfn<b.dfn); }

void insert(int u,int v){e[++pn].to=v; e[pn].next=head[u]; head[u]=pn;}

void dfs(int x){
    p[x].dfn=++cnt;
    if(head[x]) p[x].l=cnt+1; else p[x].l=cnt;
    for(int i=head[x];i;i=e[i].next) dfs(e[i].to);
    p[x].r=cnt;
}

void push_up(int i){
    if(tree[i<<1].mx>tree[i<<1|1].mx)
        tree[i].mx=tree[i<<1].mx,tree[i].id=tree[i<<1].id;
    else tree[i].mx=tree[i<<1|1].mx,tree[i].id=tree[i<<1|1].id;
}

void build(int left,int right,int i){
    tree[i].l=left; tree[i].r=right;
    if(left==right){
        tree[i].mx=-1;
        tree[i].id=-1;
        return;
    }int mid=(left+right)>>1;
    build(left,mid,i<<1); build(mid+1,right,i<<1|1);
    push_up(i);
}

void find(int left,int right,int i,int x){
    if(left==right && x==left){
        tree[i].mx=ro[fd[x]];
        tree[i].id=fd[x];
        return;
    }int mid=(left+right)>>1;
    if(x<=mid) find(left,mid,i<<1,x);
    else find(mid+1,right,i<<1|1,x);
    push_up(i);
}

void query(int left,int right,int L,int R,int i,int x){
    if(L<=left && R>=right){
        if(tree[i].mx>mx[x])
            mx[x]=tree[i].mx,ans[x]=tree[i].id;
        return;
    }int mid=(left+right)>>1;
    if(L<=mid) query(left,mid,L,R,i<<1,x);
    if(R>=mid+1) query(mid+1,right,L,R,i<<1|1,x);
}

int main(){
    n=getint(); m=getint();
    for(int i=1;i<n;i++) ans[i]=-1;
    for(int i=1;i<n;i++){
        int fa=getint(); ro[i]=getint(),p[i].ab=getint();
        insert(fa,i); p[i].id=i; p[i].ro=ro[i];
    }dfs(0); build(1,cnt,1); sort(p+1,p+n,cmp);
    for(int i=1;i<=cnt;i++) fd[p[i].dfn]=p[i].id;
    for(int i=1;i<cnt;i++)
        query(1,cnt,p[i].l,p[i].r,1,p[i].id),find(1,cnt,1,p[i].dfn);
    while(m--){
        int q=getint();
        printf("%d\n",ans[q]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值