[ZJOI2004]树的果实

森林中生长着许多奇特的果树,它们不仅外形独特,其果实更是可口。这天,两只小虫Nileh和Nixed决定一起分享一颗果树。他们从早晨一直辛勤工作到下午,终于把这颗果树锯倒。他们观察着这颗果树,果树开始端是露出地面的根部,接着像其他果树一样,有着诸多分叉(如图所示就是一颗果树),在每个分叉处生长着果实,自然Nileh和Nixed的食物就是这些果实了!他们准备把果树分成两部分,每个虫虫得到各自的一部分,而分果树的方法就是选择一个分叉点,虫虫将他们咬断(自然分叉点上的果实也被扔掉了),这样果树就变成了两个部分(每个部分不一定是连在一起的):分叉点上面的部分和分叉点下面的部分。Nileh和Nixed就会各自选一个部分吃啦!比如对于左边这颗果树,如果他们咬掉蓝色的果子,那么就被分为红色和黄色的两个部分。

考虑到被咬掉的果子会被浪费,他们想尽可能的减少浪费,于是虫虫给每个果子一个美味值,对于每个果子,他们决定计算如果咬掉这个果子,上面部分、下面部分和从树根到这个分叉点的路径中比这个果子更美味的果子各有多少个。他们以此来选择最终要被咬掉的果子是哪一个。遗憾的是果树可能很庞大,而小虫几乎是不会计算的,身为程序员的你帮帮他们吧。

【输入格式】

输入文件第一行一个整数n(n<=10^5)表示树的分叉数(包括树根)。

输入文件的第i(2<=i<=n)行一个数pi,表示分叉i的上一级分叉的编号(pi<i)。(1号分叉即树根,它没有上级分叉点)

输入文件的第n+i(1<=i<=n)行一个正整数ai,表示生长在i号分叉点上的果实的美味值。(每个果子的美味值不相等)

【输出格式】

输出共n行,每行三个数,分别表示咬掉第i个果实后上面部分、下面部分、从树根到这个分叉点的路径中比它美味的果实数。

【样例输入】

4

1

1

2

2

4

1

3

【样例输出】

2 0 0

0 0 0

0 3 1

0 1 1


树上除其子孙节点外比该节点大的节点总数:直接排序,在待统计节点前的与该节点权值不同的个数再减去问题1的答案即为所求。

从根节点到该节点路径中比该节点大的节点总数:以权值为关键字构造线段树(若权值大可行离散化处理),深度优先遍历树上节点,用栈记录下到节点的路径,并把当前节点插入线段树,在线段树中我们记录区间的元素个数,当前节点权值到最大权值这个区间中元素个数即为所求,我们再递归处理子树,在子树访问完毕后还须把该节点从线段树中删除。

那么我们如何保证当前列表中的元素权值都比k的权值大呢?

我们重新组织数据:所有元素按从大到小的顺序排序。然后依次处理每一个元素:先取得所在区间的元素个数,再将该元素插入。

我们一个很巧妙的方法:从大到小地向线段树里面加入元素,然后统计区间个数。

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn=100000+10;
struct T{
	int num,id;
	bool operator<(const T& a)const{
		return num>a.num;
	}
}A[maxn];
int ans1[maxn];
int ans2[maxn];
int ans3[maxn];
int num[maxn];
int rank[maxn];
int s[maxn<<2]={0};
int list[maxn]={0};
int N=0;
int pla[maxn];
vector<int>C[maxn];
int v[maxn];
int n;
int size[maxn]={0};
inline int lowbit(int x){
	return x&(-x);
}
inline void add(int x,int nu){
	for(;x<=n;x+=lowbit(x))
		num[x]+=nu;
}
inline int sum(int x){
	int ans=0;
	for(;x>=1;x-=lowbit(x))
		ans+=num[x];
	return ans;
}
inline void dfs(int x){
	list[++N]=x;
	pla[x]=N;
	ans1[x]=sum(rank[x]);
	add(rank[x],1);
	size[x]=1;
	for(int i=0;i<C[x].size();i++){
		int u=C[x][i];
		dfs(u);
		size[x]+=size[u];
	}
	add(rank[x],-1);
}
inline void insert(int o,int l,int r,int x){
	if(l==x&&r==x){
		s[o]++;
		return ;
	}
	else if(l!=r){
		int mid=(l+r)>>1;
		if(mid>=x)
			insert(o<<1,l,mid,x);
		if(mid<x)
			insert(o<<1|1,mid+1,r,x);
		s[o]=s[o<<1]+s[o<<1|1];
	}
}
int ql,qr;
inline int summ(int o,int l,int r){
	if(l>=ql&&r<=qr){
		return s[o];
	}
	else if(l!=r){
		int ans=0;
		int mid=(l+r)>>1;
		if(mid>=ql)
			ans+=summ(o<<1,l,mid);
		if(mid<qr)
			ans+=summ(o<<1|1,mid+1,r);
		return ans;
	}
}
int main(){
	freopen("treesfruits.in","r",stdin);
	freopen("treesfruits.out","w",stdout);
	scanf("%d",&n);
	int x;
	for(int i=2;i<=n;i++){
		scanf("%d",&x);
		C[x].push_back(i);
	}
	for(int i=1;i<=n;i++){
		scanf("%d",&v[i]);
		A[i].num=v[i];
		A[i].id=i;
	}
	sort(A+1,A+n+1);
	for(int i=1;i<=n;i++){
		ans2[A[i].id]=i-1;
		rank[A[i].id]=i;
	}
	dfs(1);
	for(int i=1;i<=n;i++){
		ql=pla[A[i].id],qr=pla[A[i].id]+size[A[i].id]-1;
		ans3[A[i].id]=summ(1,1,n);
		insert(1,1,n,pla[A[i].id]);
	}
	for(int i=1;i<=n;i++)
		printf("%d %d %d\n",ans3[i],ans2[i]-ans3[i],ans1[i]);
return 0;	
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值