【kuangbin带你飞】专题7 线段树 J - Assign the task

There is a company that has N employees(numbered from 1 to N),every employee in the company has a immediate boss (except for the leader of whole company).If you are the immediate boss of someone,that person is your subordinate, and all his subordinates are your subordinates as well. If you are nobody's boss, then you have no subordinates,the employee who has no immediate boss is the leader of whole company.So it means the N employees form a tree. 

The company usually assigns some tasks to some employees to finish.When a task is assigned to someone,He/She will assigned it to all his/her subordinates.In other words,the person and all his/her subordinates received a task in the same time. Furthermore,whenever a employee received a task,he/she will stop the current task(if he/she has) and start the new one. 

Write a program that will help in figuring out some employee’s current task after the company assign some tasks to some employee.

Input

The first line contains a single positive integer T( T <= 10 ), indicates the number of test cases.

For each test case: 

The first line contains an integer N (N ≤ 50,000) , which is the number of the employees. 

The following N - 1 lines each contain two integers u and v, which means the employee v is the immediate boss of employee u(1<=u,v<=N). 

The next line contains an integer M (M ≤ 50,000). 

The following M lines each contain a message which is either 

"C x" which means an inquiry for the current task of employee x 

or 

"T x y"which means the company assign task y to employee x. 

(1<=x<=N,0<=y<=10^9)

Output

For each test case, print the test case number (beginning with 1) in the first line and then for every inquiry, output the correspond answer per line.

Sample Input

1 
5 
4 3 
3 2 
1 3 
5 2 
5 
C 3 
T 2 1
 C 3 
T 3 2 
C 3

Sample Output

Case #1:
-1 
1 
2

题意

n行u,v,v是u的顶层上司,m行命令,T是分配任务,C是查看任务,如果给x分配任务,那么他的直接下属和间接下属都会分配到该任务

思路

线段树专题,开始实在没想到这怎么搞成线段树,下属分散不知道怎么整成一个区间

看别人的blog是线段树+dfs序,我先去了解了这个dfs序

https://blog.csdn.net/qq_24489717/article/details/50569644

其余的看代码注释

我打代码比较繁杂,可以看别人的https://blog.csdn.net/qq_41428565/article/details/82460207虽然我也没看

代码

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#define rep(i,s,e) for(int i=s;i<=e;i++)
#define rep1(i,s,e) for(int i=s;i<e;i++)
#define rep2(i,s,e,c) for(int i=s;i>=e;i--)
#define pfi(x) printf("%d\n",x)
#define pfl(x) printf("%lld\n",x)
#define pfn() printf("\n")
#define pfs() printf(" ")
#define sfi(x) scanf("%d",&x)
#define sfi1(x,y) scanf("%d%d",&x,&y)
#define sff(x) scanf("%lf",&x)
#define sfl(x) scanf("%lld",&x)
#define memset1(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long ll;
const int MAX = 5e4 + 500;
const int mod = 996873654;
vector<int> vp[MAX];
int used[MAX],st[MAX],ed[MAX];
int index=0;
struct node{
	int task;//正在执行什么工作 
	int p;//该区间属于哪个职员 
	int l,r;
	int flag,flag1;//task和p的懒惰标记 
}t[MAX<<2];
void dfs(int k){
	st[k]=++index;
	for(int i=0;i<vp[k].size();i++){//根据题意是有向无环简单树 
		dfs(vp[k][i]);
	}
	ed[k]=index;
}
void build(int left,int right,int k=1){
	t[k].l=left; t[k].r=right; t[k].flag1=t[k].p=0; t[k].task=t[k].flag=-1; 
	if(left==right) return;
	int mid=(left+right)>>1;
	build(left,mid,k<<1);
	build(mid+1,right,k<<1|1);
}
void down1(int k){
	t[k<<1].flag1=t[k<<1|1].flag1=t[k<<1].p=t[k<<1|1].p=t[k].flag1;
	t[k].flag1=0;
}
void next_build(int left,int right,int p,int k=1){
	if(t[k].l>right || t[k].r<left) return;
	if(left<=t[k].l&&t[k].r<=right){
		t[k].p=p;
		t[k].flag1=p;
		return;
	}
	if(t[k].flag1) down1(k);
	int mid=(t[k].l+t[k].r)>>1;
	if(left<=mid) next_build(left,right,p,k<<1);
	if(right>mid) next_build(left,right,p,k<<1|1);
}
void down(int k){
	t[k<<1].flag=t[k<<1|1].flag=t[k<<1].task=t[k<<1|1].task=t[k].flag;
	t[k].flag=-1;
}
void updates(int left,int right,int v,int k=1){
	if(t[k].l>right || t[k].r<left) return;
	if(left<=t[k].l&&t[k].r<=right){
		t[k].task=v;//找到子区间,直接覆盖 
		t[k].flag=v;
		return;
	}
	if(t[k].flag1) down1(k);
	if(t[k].flag!=-1) down(k);
	int mid=(t[k].l+t[k].r)>>1;
	if(left<=mid) updates(left,right,v,k<<1);
	if(right>mid) updates(left,right,v,k<<1|1);
}
int query(int left,int right,int p,int k=1){
	if(t[k].l>right || t[k].r<left) return -1;
	if(left<=t[k].l&&t[k].r<=right&&t[k].p==p){
		return t[k].task;//如果找到一个区间和职员编号匹配,返回 
	}
	if(t[k].l==t[k].r) return -1;//如果找到叶结点还不行,返回-1 
	if(t[k].flag1) down1(k);
	if(t[k].flag!=-1) down(k);
	int mid=(t[k].l+t[k].r)>>1;
	int ans1=-1,ans2=-1;//初始化-1,否则如果左区间或右区间不需要查找,会干扰max 
	if(left<=mid) ans1=query(left,right,p,k<<1);
	if(right>mid) ans2=query(left,right,p,k<<1|1);
	return max(ans1,ans2);
}
int main(){
	int T,k=0;
	sfi(T);
	while(T--){
		printf("Case #%d:\n",++k);
		memset1(used);
		index=0;
		int n;
		sfi(n);
		for(int i=0;i<=n;i++) vp[i].clear();
		for(int i=0;i<n-1;i++){
			int u,v;
			sfi1(u,v);
			vp[v].push_back(u);
			used[u]=1;
		}
		//虚设一个0为总BOSS 
		for(int i=1;i<=n;i++){
			if(!used[i]){//如果不是别人的下属,设置它的上司为0 
				vp[0].push_back(i);
			}
		}
		dfs(0);
		build(1,index);//初步初始化 
		queue<int> q;//此处从顶层上司开始给每一个区间添加属于哪一个职员 
		q.push(0);
		while(q.size()){//按顺序添加保证不会弄混 
			int x=q.front();		
			q.pop();
			next_build(st[x],ed[x],x);//进一步初始化区间属于哪个职员 
			for(int i=0;i<vp[x].size();i++){
				q.push(vp[x][i]);
			}
		}
		int m;
		sfi(m);
		char chars[3]; int x;
		for(int i=0;i<m;i++){
			scanf("%s%d",chars,&x);
			if(chars[0]=='C'){
				pfi(query(st[x],ed[x],x));
			}
			else{
				int y;
				sfi(y);
				updates(st[x],ed[x],y);
			}
		}
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
并查集是一种常用的数据结构,用于管理一个不相交集合的数据。在并查集中,每个元素都有一个父节点指向它所属的集合的代表元素。通过查找和合并操作,可以判断两个元素是否属于同一个集合,并将它们合并到同一个集合中。 在解决某些问题时,可以使用并查集进行优化。例如,在区间查询中,可以通过优化并查集的查询过程,快速找到第一个符合条件的点。 对于拆边(destroy)操作,一般的并查集无法直接实现这个功能。但是可以通过一个巧妙的方法来解决。首先,记录下所有不会被拆除的边,然后按照逆序处理这些指令。遇到拆边操作时,将该边重新加入并查集中即可。 在实现并查集时,虽然它是一种树形结构,但只需要使用数组就可以实现。可以通过将每个元素的父节点记录在数组中,来表示元素之间的关系。通过路径压缩和按秩合并等优化策略,可以提高并查集的效率。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [「kuangbin专题五并查集专题题解](https://blog.csdn.net/weixin_51216553/article/details/121643742)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [并查集(详细解释+完整C语言代码)](https://blog.csdn.net/weixin_54186646/article/details/124477838)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值