HDU4366 Successor【线段树】

博客围绕公司人员替换问题展开,给出表示上下级关系的树,每个节点有忠诚度和能力值。有m个询问,需找出节点P子树中能力值大于P且忠诚度最大的节点。采用线段树维护区间忠诚度最大值,结合DFS遍历和节点排序解决问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

S u c c e s s o r Successor Successor

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

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
2012 Multi-University Training Contest 7

题意:

给出一棵表示上下级关系的树,每个节点(除了根节点之外)都有一个Loyalty值和Ability值,给出m个询问,每次要求计算出节点P的子树中,Ability值大于节点P的节点并且Loyalty值最大的那个节点。

TJ:

要求Loyalty最大的值,可以用线段树来维护区间Loyalty的最大值,但是维护的区间边界是什么,题目给出的是一棵关系树,对于DFS来说,子树的遍历顺序肯定在当前节点之后,给每个节点两个顺序编号,分别是遍历进入以该节点为根的树的DFS遍历序号和出该节点的遍历序号,就可以得到一个线性序列,之后只要维护这个线性序列的Loyalty的最大值就好了。
但是题目给出了另一个约束条件,就是子节点的Ability值必须要大于当前节点,对于这个要求,可以先把所有点按各点的Ability值从大到小进行排序,然后按顺序把各个节点的Loyalty值更新到线段树中去,按这样的顺序,每次查询的时候,有Loyalty值的点的Ability值肯定大于当前需要查询的点(注意Ability值相同的点要同时查询再更新),这样就可以把每个点的答案都记录下来(记录下来的是一个Loyalty值),由于每个Loyalty值都是不一样的,所以可以把Loyalty值和节点的标号一一映射起来,要查询的节点标号就可以通过map把Loyalty值转换过去了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
const int maxn = 5e4+7;
map<int,int> mp;
int T,n,m,head[maxn],edge_num,in[maxn],out[maxn],number,res[maxn];
struct EDGE{
	int to,next;
}edge[maxn];
struct EMP{
	int id,loyal,ability;
	bool operator < (const EMP &x){
		return ability>x.ability;
	}
}emp[maxn];
struct Segment_Tree{
	int maxx[maxn<<2];
	void push_up(int rt){
		maxx[rt] = max(maxx[rt<<1],maxx[(rt<<1)+1]);
	}
	void update(int l,int r,int pos,int rt,int val){
		if(l+1==r){
			maxx[rt] = val;
			return;
		}
		int mid = (l+r)>>1;
		if(pos<mid) update(l,mid,pos,rt<<1,val);
		else update(mid,r,pos,(rt<<1)+1,val);
		push_up(rt);
	}
	int query(int l,int r,int ql,int qr,int rt){
		if(ql<=l && r<=qr) return maxx[rt];
		if(l>=qr || ql>=r) return -1;
		int mid = (l+r)>>1;
		return max(query(l,mid,ql,qr,rt<<1),query(mid,r,ql,qr,(rt<<1)+1));
	}
}Seg_Tree;
void init(){
	memset(Seg_Tree.maxx,255,sizeof(Seg_Tree.maxx));
	mp.clear(); mp.insert(pair<int,int>(-1,-1));
	memset(head,255,sizeof(head));
	edge_num = 0;
}
void add_edge(int u,int v){
	edge[edge_num].to = v;
	edge[edge_num].next = head[u];
	head[u] = edge_num++;
}
void dfs(int now){
	in[now] = number++;
	for(int i=head[now];i!=-1;i=edge[i].next) dfs(edge[i].to);
	out[now] = number;
}
void pre_done(){
	number = 0;
	dfs(0);
	sort(emp+1,emp+n);
	for(int i=1,now=1;i<n;i++){
		int id = emp[i].id;
		res[id] = mp[Seg_Tree.query(1,number,in[id],out[id],1)];
		if(i!=n-1&&emp[i+1].ability==emp[i].ability) continue;
		for(;now!=i+1;now++){
			int nid = emp[now].id;
			Seg_Tree.update(1,number,in[nid],1,emp[now].loyal);
		}
	}
}
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d %d",&n,&m);
		init();
		for(int i=1;i<n;i++){
			int pre;
			scanf("%d %d %d",&pre,&emp[i].loyal,&emp[i].ability);
			add_edge(pre,i);
			mp.insert(pair<int,int>(emp[i].loyal,i));
			emp[i].id = i;
		}
		pre_done();
		for(int i=1;i<=m;i++){
			int pos;
			scanf("%d",&pos);
			printf("%d\n",res[pos]);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值