1915: 我能做(set维护线段树)

duxing201606毕业后子承父业继承了一家公司。在公司中,除了duxing201606以外的员工都有一个上司。如果X是Y的上司,Y是Z的上司,那么Y和Z都是X的下属。
duxing201606会经常分配任务给员工,duxing201606非常喜欢团队去完成任务,所以每次duxing201606给X分配任务的时候,会同时给X的所有下属都分配这个任务,该任务的编号为X。
duxing201606想知道如果下达多个命令的话,公司会不会乱套。duxing201606一共会下达3种命令:
1 x 给员工x和他的下属都分配编号为X的任务。
2 x 员工x和他的下属都完成了编号为X的任务。
3 x 输出员工x所有任务中编号最小的那个任务。 如果员工x没有任务就输出-1,否则输出最小的任务编号。

输入

第一行为 n m,代表有n个员工, duxing201606会下达m条命令( 1 <= n, m <= 5e4)。
第二行 n-1个p[i],p[i]代表的是 第 i+1 号员工的上司是谁,( 1<= p[i] <= i)。
第3行到m+2行,每一行输入一个 op x,含义如上。

输出

对于每条命令3,输出答案。

样例输入

9 9
1 2 2 1 5 5 5 8
3 1
3 6
1 5
3 5
3 6
1 1
3 6
2 1
3 6

样例输出

-1
-1
5
5
1
5

正解是:
线段树维护子树。
先求出dfs序。
每次1 X的时候,都往 in[x] out[x]这段区间内插入x
每次2 X的时候,都从 in[x] out[x]这段区间内删除x
每次3 X的时候,和李超树类似,在包含这个in[x]的所有区间中找到最小值。

最小值用set去维护。

直接将线段树的每个区间转化为set,注意这里由于用dfs序直接修改线段树的区间,所以将包含这个数的线段树区间全部更新就行,不用更新到最低,不然会mle。查询同理,因为上面区间一定包含查找点,所以一直查找到这个点最底部,得到答案,这里类似李超树的查询(感觉两个都差不多)

#include<bits/stdc++.h>
#define fi first
#define se second
#define log2(a) log(n)/log(2)
#define show(a) cout<<a<<endl;
#define show2(a,b) cout<<a<<" "<<b<<endl;
#define show3(a,b,c) cout<<a<<" "<<b<<" "<<c<<endl;
using namespace std;

typedef long long ll;
typedef pair<ll, ll> P;
typedef pair<P, ll> LP;
const ll inf = 1e9 + 10;
const int N = 1e4 + 10;
const ll mod = 1e9+7;
const int base=131;
const double pi=acos(-1);



vector<int> v[N];
set<int> st[N<<2];
int in[N],out[N],num[N];
int n,m;
int ans,cnt;

void dfs(int x)
{
	in[x]=++cnt;
	num[cnt]=x;
	for(auto to:v[x])
	{
		dfs(to);
	}
	out[x]=cnt;
}
void init()
{
	dfs(1);
}

void add(int val,int ql,int qr,int rt,int l,int r)
{
	if(ql<=l&&qr>=r)
	{
		st[rt].insert(val);
		return ;
	}
	int m=l+r >> 1;
	if(ql<=m) add(val,ql,qr,rt<<1,l,m);
	if(qr>m) add(val,ql,qr,rt<<1|1,m+1,r);
}

void red(int val,int ql,int qr,int rt,int l,int r)
{
	if(ql<=l&&qr>=r)
	{
		if(st[rt].count(val))
		{
			st[rt].erase(val);
			
		}
		return ;
	}
	int m=l+r >> 1;
	if(ql<=m) red(val,ql,qr,rt<<1,l,m);
	if(qr>m) red(val,ql,qr,rt<<1|1,m+1,r);
}

void query(int x,int rt,int l,int r)
{

	if(st[rt].size())
	{
		ans=min(ans,*st[rt].begin());
	}


	if(l==r) return ;
	int m=l+r >> 1;
	if(x<=m) query(x,rt<<1,l,m);
	if(x>m) query(x,rt<<1|1,m+1,r);
}

int main( )
{

	scanf("%d%d",&n,&m);
	int x,y;

	for(int i=2;i<=n;i++)
	{
		scanf("%d",&x);
		v[x].push_back(i);
	}
	init();

	while(m--)
	{
		scanf("%d%d",&x,&y);
		if(x==1)
		{
			add(y,in[y],out[y],1,1,n);
		}
		else if(x==2)
		{
			red(y,in[y],out[y],1,1,n);
		}
		else
		{
			ans=inf;
			query(in[y],1,1,n);
			if(ans!=inf)  printf("%d\n",ans);
			else puts("-1");
		}

	}







}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值