2019 徐州网络赛 F. Little M's attack plan 树上倍增+dfs序+二分

题目链接:https://nanti.jisuanke.com/t/41388

题解:这个题比赛最后也没改对,训练时突然想到有个地方写了emmmm

思路:我们先按照dfs序把每个点跑出来,然后vector记录下每个深度的点,很容易就可以想到,对于每个深度,距离x小于等于k的那些点肯定是在vector内的一段连续的序列,因此我们就可以二分找到最左最右的位置,求权值的话,我们维护一个前缀和就好了,但要注意一点就是,我们要先向上求,记录一下祖先向下延伸最远的点

比如:

1 2

1 3

3 4

这个图,再求距离2小于等于3的点时,直接从2向下是不行的,因为父节点1向下延伸的更长。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
struct node {
	int to, nex;
}e[N * 2];
int head[N], len;
vector<int> v[N];
int dep[N];
int maxdep;
int n, m;
ll val[N];
vector<ll> sum[N];
int dp[N][22];
int son[N], nex[N];
void add(int x, int y) {
	e[len].to = y;
	e[len].nex = head[x];
	head[x] = len++;
}
int pos[N];
int lenv[N];
void dfs1(int u, int fa) {
	dep[u] = dep[fa] + 1;
	maxdep = max(maxdep, dep[u]);
	v[dep[u]].push_back(u);
	pos[u] = lenv[dep[u]]++;
	dp[u][0] = fa;
	for(int i = 1; i < 22; i++)
		if(dp[u][i - 1])
			dp[u][i] = dp[dp[u][i - 1]][i - 1];
		else
			break;
	int to;
	int flag = 0;
	for(int i = head[u]; i != -1; i = e[i].nex) {
		to = e[i].to;
		if(to == fa) continue;
		dfs1(to, u);
		flag = 1;
		
		if(son[to] > son[nex[u]]) {
			nex[u] = to;
		}
	}
	if(!flag) son[u] = 1;
	else son[u] = son[nex[u]] + 1;
}
bool judge(int x, int y, int k) {
	k--;
	int cntx = x, cnty = y;
	if(dep[x] < dep[y]) swap(x,y);
    int tmp = dep[x] - dep[y];
    for(int i = 0; i < 22; i++)
        if(tmp & ( 1 << i))
            x = dp[x][i];
    if(x == y) {
    	return dep[cntx] + dep[cnty] - 2 * dep[x] <= k;
	}
    for(int i = 21; i >= 0; i--){
        if(dp[x][i] != dp[y][i]) {
            x = dp[x][i];
            y = dp[y][i];
        }
    }
    x = dp[x][0];
    return dep[cntx] + dep[cnty] - 2 * dep[x] <= k;
}
int main() {
	scanf("%d", &n);
	memset(head, -1, sizeof(head));
	for(int i = 1; i <= n; i++) scanf("%lld", &val[i]);
	int x, y, k;
	for(int i = 1; i < n; i++) {
		scanf("%d %d", &x, &y);
		add(x, y);
		add(y, x);
	}
	dfs1(1, 0);
	scanf("%d", &m);
	ll cnt = 0;
	for(int i = 1; i <= maxdep; i++) {
		cnt = 0;
		for(int j = 0; j < v[i].size(); j++) {
			cnt += val[v[i][j]];
			sum[i].push_back(cnt);
		}
	}
	int l, r, mid;
	int ansl, ansr;
	ll ans;
	int tmpk;
	int tt;
	int maxx;
	int tx;
	int id;
	while(m--) {
		scanf("%d %d", &x, &k);
		k++;
		y = x;
		ans = 0;
		tmpk = k;
		maxx = son[x] - 1;
		id = x;
		tt = 0;
		tx = 0;
		while(y && tmpk) {
			tmpk--;
			l = 0, r = pos[y];
			if(min(son[y] , k - tt ) - 1 > maxx + tt) {
				maxx = min(son[y] , k - tt ) - 1 - tt;
				id = y;
				tx = tt;
			}
			tt++;
			while(l <= r) {
				mid = (l + r) >> 1;
				if(judge(v[dep[y]][mid], x, k)) {
					ansl = mid;
					r = mid - 1;
				} else {
					l = mid + 1;
				}
			} 
			
			l = pos[y], r = lenv[dep[y]] - 1;
			while(l <= r) {
				mid = (l + r) >> 1;
				if(judge(v[dep[y]][mid], x, k)) {
					ansr = mid;
					l = mid + 1;
				} else {
					r = mid - 1;
				}
			} 
			if(ansl == 0) cnt = 0;
			else cnt = sum[dep[y]][ansl - 1];
			ans += sum[dep[y]][ansr] - cnt;
			y = dp[y][0];
		}
		if(tx > 0) {
			while(tx) {
				tx--;
				id = nex[id];
			}
		}
		
		y = nex[id];
		tmpk = k - 1;
		int maxx, id;
		while(y && tmpk) {
			tmpk--;
			l = 0, r = pos[y];
            ansl = -1;
            ansr = -1;
			while(l <= r) {
				mid = (l + r) >> 1;
				if(judge(v[dep[y]][mid], x, k)) {
					ansl = mid;
					r = mid - 1;
				} else {
					l = mid + 1;
				}
			} 
			
			l = pos[y], r = lenv[dep[y]] - 1;
			while(l <= r) {
				mid = (l + r) >> 1;
				if(judge(v[dep[y]][mid], x, k)) {
					ansr = mid;
					l = mid + 1;
				} else {
					r = mid - 1;
				}
			} 
            if(ansl == -1 || ansr == -1) break;
			if(ansl == 0) cnt = 0;
			else cnt = sum[dep[y]][ansl - 1];
			ans += sum[dep[y]][ansr] - cnt;
			y = nex[y];
		}
		
		
		printf("%lld\n", ans);
	} 
	
	return 0;
}
/*
10
1 1 1 1 1 1 1 1 1 1
1 2
1 3
2 4
3 5
3 6
3 7
4 8
8 10
6 9
5

7
1 1 1 1 1 1 1 
1 2
1 3
2 4
2 5
3 6
3 7
5
1 100
1 0
2 1
4 100
4 2

10
1 1 1 1 1 1 1 1 1 1
1 2
1 3
2 4
2 5
3 6
3 7
1 8
9 10
8 9
5


6
4 5 2 1 2 1
1 4
1 3
5 3
6 2
3 6
5
*/

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于计算机专业的学生而言,参加各类比能够带来多方面的益处,具体包括但不限于以下几点: 技能提升: 参与比促使学生深入学习和掌握计算机领域的专业知识与技能,如编程语言、算法设计、软件工程、网络安全等。 比通常涉及实际问题的解决,有助于将理论知识应用于实践中,增强问题解决能力。 实践经验: 大多数比都要求参者设计并实现解决方案,这提供了宝贵的动手操作机会,有助于积累项目经验。 实践经验对于计算机专业的学生尤为重要,因为雇主往往更青睐有实际项目背景的候选人。 团队合作: 许多比鼓励团队协作,这有助于培养学生的团队精神、沟通技巧和领导能力。 团队合作还能促进学生之间的知识共享和思维碰撞,有助于形成更全面的解决方案。 职业发展: 获奖经历可以显著增强简历的吸引力,为求职或继续深造提供有力支持。 某些比可能直接与企业合作,提供实习、工作机会或奖学金,为学生的职业生涯打开更多门路。 网络拓展: 比是结识同行业人才的好机会,可以帮助学生建立行业联系,这对于未来的职业发展非常重要。 奖金与荣誉: 许多比提供奖金或奖品,这不仅能给予学生经济上的奖励,还能增强其成就感和自信心。 荣誉证书或奖状可以证明学生的成就,对个人品牌建设有积极作用。 创新与研究: 参加比可以激发学生的创新思维,推动科研项目的开展,有时甚至能促成学术论文的发表。 个人成长: 在准备和参加比的过程中,学生将面临压力与挑战,这有助于培养良好的心理素质和抗压能力。 自我挑战和克服困难的经历对个人成长有着深远的影响。 综上所述,参加计算机领域的比对于学生来说是一个全面发展的平台,不仅可以提升专业技能,还能增强团队协作、沟通、解决问题的能力,并为未来的职业生涯奠定坚实的基础。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值