太戈Q974. 公司破事之二

传送门:Q974

题目描述

lester是"VVJFJ方熊玥题"公司的老板,共有n名员工,工号分别为0...n-1(lester本人是0号员工)。公司是一个官僚体系,除lester外每个人有且仅有一个直属上级。为了营(zhi)造(zao)氛(hun)围(luan),lester决定不时让一些员工休假。刚开始时,所有员工都是工作状态,lester会依次下发m条指令,每条指令由两个正整数t,x表示。具体含义如下

t=1,员工x及其所有上级(包括间接上级)停止工作,开始休假。如其中有人已经在休假状态,则保持休假

t=2,员工x及其所有下级(包括间接下级)结束休假,开始工作。如其中有人已经在工作状态,则保持工作

(聪明的你会发现根据这种规则,lester自己大部分时间都在休假^^)

现在lester想知道每次指令下发以后,会导致多少人的状态发生改变(即由工作变为休假,或由休假变为工作)

输入格式

输入文件company2.in 第1行一个正整数n(<=100000)
第2行n-1个整数,分别表示员工1..n-1的直属上级编号(数据保证上级关系不会形成环)
第3行1个正整数m(<=100000)
后面m行每行2个整数t,x(t=1,2,0<=x<=n-1)

输出格式

输出文件company2.out 输出m行每行1个整数,表示答案

输入输出样例

输入样例#1:

4

0 1 1

3

1 2

1 3

2 1

输出样例#1:

3

1

3

思路

用线段树(重链剖分)+分块可以解决这道题,但注意原题给的是0~n-1,要改成1~n

还有一些注意点我将会在后续提到

代码

1.主函数

cin>>n;
	for(ll i=2;i<=n;i++){
		cin>>p[i];
		p[i]++;//把编号从0~n-1,改为1~n 
		adde(p[i],i);
	}
	dfs_son(1);//初始化 
	dfs_top_t(1);
	for(ll i=1;i<=n;i++){
		a[i]=1;
	}
	B=sqrt(n)+1;
//	B=1;
	for(ll i=1;i<=n;i++){
		b[i]=(i-1)/B+1;
//		cout<<p[i]<<endl;
	}
//	cout<<"******";
	for(ll i=1;i<=n;i++){
		bs[b[i]]+=a[i];
	}
	for(ll i=1;i<=b[n];i++){
		tag[i]=2;
	}
	cin>>m;
	for(ll i=1;i<=m;i++){
		ll t,x;
		cin>>t>>x;
		x++;//把编号从0~n-1,改为1~n 
		if(t==1){
			hld(x);
		}else{
			wrk(x);
		}
	}

主要是重链剖分+分块初始化。

2.重链剖分函数部分

void dfs_son(ll u){
	d[u]=d[p[u]]+1;
	sz[u]=1;
	son[u]=0;
	for(ll i=hd[u];i;i=nxt[i]){
		ll v=to[i];
		dfs_son(v);
		sz[u]+=sz[v];
		if(sz[son[u]]<sz[v]){
			son[u]=v;
		}
	}
}
void dfs_top_t(ll u){
	tI[u]=++timer;
	tO[u]=tI[u]+sz[u]-1;
	if(son[p[u]]==u){
		top[u]=top[p[u]];
	}else{
		top[u]=u;
	}
	if(!son[u]){
		return;
	}
	dfs_top_t(son[u]);
	for(ll i=hd[u];i;i=nxt[i]){
		ll v=to[i];
		if(v==son[u]){
			continue;
		}
		dfs_top_t(v);
	}
}

不用多说,模板。

3.分块

1)信息下放

把tag[iB]的标记暴力赋值给块中a[i](tag为2则不用操作,因为没有操作

其中a[i]表示i的状态:

0表示在休假;

1表示在工作。

void pushdown(ll iB){//信息下放 
	if(tag[iB]==2){
		return;
	}
	ll l=B*(iB-1)+1,r=min(n,B*iB);//防止越界 
	for(ll u=l;u<=r;u++){
		a[u]=tag[b[u]];//***不是tag[u],应为tag[iB] or tag[b[u]]
	}
	tag[iB]=2;
}

!!!tag[iB]是每块的状态!

 2)改变状态

分为wrk(x),hld(x)两种(下级工作或上级休假)

void hld(ll x){//上级休假 
	ll ans=0;
	while(x){
		ans+=rsq(tI[top[x]],tI[x]);
		upd(tI[top[x]],tI[x],0);
		x=p[top[x]];
	}
	cout<<ans<<endl;
}
void wrk(ll x){//下级工作 
	ll ans=sz[x]-rsq(tI[x],tO[x]);
	upd(tI[x],tO[x],1);
	cout<<ans<<endl;
}
3)操作后的查询,更新

即upd()和rsq()

void upd(ll l,ll r,ll w){//更新 
	pushdown(b[l]);
	pushdown(b[r]);
	if(b[l]==b[r]){
		for(ll i=l;i<=r;i++){
			bs[b[i]]+=w-a[i];
			a[i]=w;
		}
		return ;
	}
	for(ll i=b[l]+1;i<b[r];i++){
		tag[i]=w;
		bs[i]=w*B;
	}
	for(ll i=l;b[i]==b[l];i++){
		bs[b[i]]+=w-a[i];
		a[i]=w;
	}
	for(ll i=r;b[i]==b[r];i--){
		bs[b[i]]+=w-a[i];
		a[i]=w;
	}
}
ll rsq(ll l,ll r){//查询工作人数 
	pushdown(b[l]);
	pushdown(b[r]);
	ll ans=0;
	if(b[l]==b[r]){
		for(ll i=l;i<=r;i++){
			ans+=a[i];
		}
		return ans;
	}
	for(ll i=b[l]+1;i<b[r];i++){
		ans+=bs[i];
	}
	for(ll i=l;b[i]==b[l];i++){
		ans+=a[i];
	}
	for(ll i=r;b[i]==b[r];i--){
		ans+=a[i];
	}
	return ans;
}

 以上就是这题的解析。

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值