D. Ralph And His Tour in Binary Country(二叉树性质+树形计数)

题目1

题意:

    给定一棵带权二叉树,共有m次访问。对于一次访问x,h,输出以x为起点,任意点y为终点,dis(x,y)小于h,贡献为h-dis(x,y)。
     1   ≤   n   ≤   1 0 6 , 1   ≤   m   ≤   1 0 5 , 1   ≤   A i   ≤   n , 0   ≤   H i   ≤   1 0 7 1 ≤ n ≤ 10^6, 1 ≤ m ≤ 10^5,1 ≤ A_i ≤ n, 0 ≤ H_i ≤ 10^7 1n106,1m105,1Ain,0Hi107

分析:

    根据题目我们很容易得出这是一棵满二叉树,二叉树的性质就是本题的关键点了。由于是满二叉树,所以每个节点可以维护其所有子孙节点到他的距离。对于一次访问的x和h,先处理x的子树满足条件的,满足条件的用upper_bound找,在维护一个前缀和就可以logn算出答案的贡献,然后再往父节点转移,父节点做的就是去剩下的那棵子树计算满足条件的点,再转移到他的父节点,由于满二叉树,最多logn层。

#include <iostream>
#include <vector> 
#include <algorithm>
using namespace std;

typedef long long ll;

struct node{
	int id;
	ll val;
	node(int a,ll b)
	{
		id = a;
		val = b;
	}
};

vector<node> g[1000005];
vector<ll> dis[1000005],sum[1000005];
ll v[1000005];

void dfs(int x)
{
	if( g[x].size() == 0 )
	{
		dis[x].push_back(0);
		sum[x].push_back(0);  
		return;
	}
	for (int i = 0; i < g[x].size(); i++)
	{
		int t = g[x][i].id;
		dfs(t);
		for (int j = 0; j < dis[t].size(); j++)
		{
			dis[x].push_back(dis[t][j]+g[x][i].val); 
		}
	}
	dis[x].push_back(0); 
	sort(dis[x].begin(),dis[x].end());
	ll sumx = 0;
	for (int i = 0; i < dis[x].size(); i++)
	{
		sumx += dis[x][i];
		sum[x].push_back(sumx); 
	}
}

ll cal(int x,int h)
{
	ll ans = 0;
	int t = upper_bound(dis[x].begin(),dis[x].end(),h) - dis[x].begin();
	if( t != 0 ) ans += (ll)h * t - sum[x][t-1]; 
	return ans;
}

int n;

void slove(int x,int h)
{
	ll ans = 0;
	ans += cal(x,h);
	int temp = x;
	while( temp != 1 )
	{	
		h -= v[temp-1];
		if( h <= 0 ) break;
		ans += h;
		x = temp;	
		temp /= 2;
		int t1 = temp * 2;
		int t2 = temp * 2 + 1;
		if( t2 > n ) continue;
		if( t1 == x )
		{
			ans += cal(t2,h-v[t2-1]);
		}else ans += cal(t1,h-v[t1-1]);
	}
	cout << ans << '\n';
}

int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int m;
	cin >> n >> m;
	for (int i = 1; i < n; i++)
	{
		cin >> v[i];
		g[(i+1)/2].push_back(node(i+1,v[i])); 
	}
	dfs(1);
	for (int i = 1; i <= m; i++)
	{
		int x,h;
		cin >> x >> h;
		slove(x,h);
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值