Codeforces Round #381 (Div. 2) D. Alyona and a tree

8 篇文章 0 订阅
6 篇文章 0 订阅

主要是运用树状数组快速区间求和,还有dfs求距离,记录点的顺序,该点的子树最远的叶子;


#include<bits/stdc++.h>
using namespace std;

#define pb(a) push_back(a)
#define LL long long
const int maxn = 2e5+5;
int a[maxn],in[maxn],out[maxn],c[maxn],ans[maxn];  //a-每个点的值,in-dfs该点的顺序,out-该点的子树最远的叶子
LL dis[maxn]; //每个点到根的距离
vector<int> g[maxn],w[maxn];  //g-建树,w-每条边的值
int n,ord = 0;

struct node {
	int tp,id;
	LL v;
} b[maxn*2];

bool cmp(node a,node b) {
	if(a.v == b.v) return a.tp < b.tp;
	return a.v < b.v;
}

void dfs(int x,LL d) {  //dfs in-标记每个点遍历的顺序,out-该点的子树最远的叶子,dis-该点到根节点的距离
	dis[x] = d;
	in[x] = ++ord;
	for(int i = 0;i < g[x].size();i++) {
		dfs(g[x][i],d+w[x][i]);
	}
	out[x] = ord;
}

void add(int x) {
	while(x <= n) {
		c[x]++;
		x += x&-x; //lowbit直接写在函数里面,看起来简洁些
	}
}

int query(int x) {
	int ans = 0;
	while(x) {
		ans += c[x];
		x -= x&-x;
	}
	return ans;
}

int main() {
	scanf("%d",&n);
	for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
	for(int i = 2;i <= n;i++) {
		int u,v;
		scanf("%d%d",&u,&v);
		g[u].pb(i);
		w[u].pb(v);
	}
	dfs(1,0);
	int cnt = 0;
	for(int i = 1;i <= n;i++) {
		b[++cnt] = (node){1,i,dis[i]-a[i]};
		b[++cnt] = (node){2,i,dis[i]};
	}
	//for(int i = 1;i <= cnt;i++) printf("%d  %d  %I64d\n",b[i].tp,b[i].id,b[i].v);
	sort(b+1,b+1+cnt,cmp);
	memset(c,0,sizeof(c));
	for(int i = 1;i <= cnt;i++) {
		if(b[i].tp == 1) add(in[b[i].id]);
		else {
			ans[b[i].id] = query(out[b[i].id]) - query(in[b[i].id] - 1); 
		}
	}
	for(int i = 1;i <= n;i++)
		printf("%d ",ans[i]-1);  //得到的答案包含它本身,所以要减一
	puts("");
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值