Solution:CF877E(Danil and a Part-time Job)

题目链接

Link:CF877E

解题思路

一道DFS序的好题。
DFS序想必大家都知道,就是用DFS遍历树或图时访问节点的顺序。我们一般说的DFS序,都是指树的DFS序。以下的DFS序都是从根开始的树上DFS序。

DFS序有一个重要的性质:任何一个子树,它的节点在DFS序中一定排成连续的一段。这时,我们就可以把某些修改子树+查询问题转为区间修改+查询问题。

我们这一题实际上就是线段树。先搞DFS序,记录每一个节点对应的区间,然后线段树即可。

代码实现

#include <iostream>
#include <bitset>
#include <vector>
#include <array>

using namespace std;

int cur_points;
bitset<800001> lazy_tag;
array<int,200001> val_inital,values;
array<int,800001> segment_tree;
array<vector<int>,200001> tree;
array<pair<int,int>,200001> ranges;

inline int l_son(int num)
{
	return num<<1;
}
inline int r_son(int num)
{
	return num<<1|1;
}
inline void push_up(int pos)
{
	segment_tree[pos]=segment_tree[l_son(pos)]+segment_tree[r_son(pos)];
}
void build(int left,int right,int pos)
{
	if(left==right)
		return (void)(segment_tree[pos]=values[left]);
	int mid=(left+right)>>1;
	build(left,mid,l_son(pos)),build(mid+1,right,r_son(pos));
	push_up(pos);
}
inline void set_tag(int left,int right,bool val,int pos)
{
	if(val)
		lazy_tag.flip(pos),segment_tree[pos]=right-left+1-segment_tree[pos];
}
inline void push_down(int left,int right,int pos)
{
	int mid=(left+right)>>1;
	set_tag(left,mid,lazy_tag[pos],l_son(pos));
	set_tag(mid+1,right,lazy_tag[pos],r_son(pos));
	lazy_tag.reset(pos);
}
void update(const int& q_left,const int& q_right,int n_left,int n_right,bool val,int pos)
{
	if(n_left>=q_left&&n_right<=q_right)
		return set_tag(n_left,n_right,val,pos);
	int mid=(n_left+n_right)>>1;
	push_down(n_left,n_right,pos);
	if(q_left<=mid)
		update(q_left,q_right,n_left,mid,val,l_son(pos));
	if(q_right>mid)
		update(q_left,q_right,mid+1,n_right,val,r_son(pos));
	push_up(pos);
}
long long range_sum(const int& q_left,const int& q_right,int n_left,int n_right,int pos)
{
	long long result=0;
	if(n_left>=q_left&&n_right<=q_right)
		return segment_tree[pos];
	int mid=(n_left+n_right)>>1;
	push_down(n_left,n_right,pos);
	if(q_left<=mid)
		result+=range_sum(q_left,q_right,n_left,mid,l_son(pos));
	if(q_right>mid)
		result+=range_sum(q_left,q_right,mid+1,n_right,r_son(pos));
	return result;
}

int DFS(int root)
{
	ranges[root].first=++cur_points;
	values[cur_points]=val_inital[root];
	for(const int& tmp:tree[root])
		DFS(tmp);
	ranges[root].second=cur_points; 
}

int main(int argc,char* argv[],char* envp[])
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int cnt,cntquery,tmp;
	cin>>cnt;
	for(int i=2;i<=cnt;i++)
		cin>>tmp,tree[tmp].push_back(i);
	for(int i=1;i<=cnt;i++)
		cin>>val_inital[i];
	DFS(1);
	build(1,cnt,1);
	cin>>cntquery;
	while(cntquery--)
	{
		char type;
		int param;
		(cin>>type).ignore(3,' ')>>param;
		if(type=='p')
			update(ranges[param].first,ranges[param].second,1,cnt,true,1);
		else
			cout<<range_sum(ranges[param].first,ranges[param].second,1,cnt,1)<<'\n';
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值