【蓝桥杯国赛】H 机房

蓝桥杯2022年第十三届决赛真题-机房 - C语言网 (dotcpp.com)

题意:

一共有n个结点,n-1条边,因此这是棵树

信息经过一个结点,就会产生一定的延迟,具体延迟的时间等于该结点的度数

每次询问树上两个结点,问两个结点之间路径中所有点度数之和

思路:

树上前缀和+LCA

因为是棵树,对于树上任意两个结点,路径是唯一的

多次询问,如果每次询问都去暴力跑路径的话,复杂度将会是O(qn),会超时

因此我们去求出每个结点离根节点的路径中所有结点的度数之和,相当于去预处理所有结点的前缀和

如下图,黑色的线就是x到根节点的路径,结点x的前缀和就是这条路径上所有结点度数之和

 

 黑色表示结点x前缀和

蓝色表示结点y前缀和

红色表示lca(x,y)前缀和

黄色表示lca(x,y)的父节点的前缀和

那么x到y的路径的结点的度数之和就是 in[x]+in[y]-in[lca(x,y)]-in[st[lca(x,y][0]]

其中in[x]表示结点x到根节点的路径中度数之和

那么我们如何去求每个结点离根节点的前缀和:

bfs或dfs去遍历所有结点,然后从父结点向子结点递推:

dep[u]=dep[st[u][0]]+1;

其中st[u][0]表示结点u的父结点

为了防止爆栈,用bfs比较合适

我们如何去求lca:

首先动态规划预处理st表

 然后对于树上任意两点x,y,先让x和y的深度相等

深度相等之后,让x和y以二进制拆分的倍增往上爬,直到爬到它们的最近公共祖先

Code:

#include <bits/stdc++.h>
using namespace std;
const int mxn=1e5+10,mxe=1e5+10;
struct ty{
	int to,next;
}edge[mxe<<1];
queue<int> q;
int n,m,x,y,tot=0;
int in[mxn],head[mxn],st[mxn][25],dep[mxn];
void init(){
	tot=0;
	for(int i=0;i<=n;i++){
		head[i]=-1;
		in[i]=dep[i]=0;
		for(int j=0;j<mxn;j++) st[i][j]=0;
	}
	while(!q.empty()) q.pop();
}
void add(int u,int v){
	edge[tot].to=v;
	edge[tot].next=head[u];
	head[u]=tot++;
}
void bfs(){
	q.push(1);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		dep[u]=dep[st[u][0]]+1;
		in[u]+=in[st[u][0]];
		for(int i=head[u];~i;i=edge[i].next){
			if(edge[i].to==st[u][0]) continue;
			st[edge[i].to][0]=u;
			q.push(edge[i].to);
		}
	}
}
int lca(int u,int v){
	if(dep[u]<dep[v]) swap(u,v);
	for(int i=19;i>=0;i--){
		if(dep[st[u][i]]<dep[v]) continue;
		u=st[u][i];
	}
	if(u==v) return u;
	for(int i=19;i>=0;i--){
		if(st[u][i]!=st[v][i]){
			u=st[u][i];
			v=st[v][i];
		}
	}
	return st[u][0];
}
int main(){
	scanf("%d%d",&n,&m);
	memset(head,-1,sizeof(head));
	//init();
	for(int i=1;i<=n-1;i++){
		scanf("%d%d",&x,&y);
		add(x,y);in[y]++;
		add(y,x);in[x]++;
	}
	bfs();
	for(int len=1;len<20;len++){
		for(int i=1;i<=n;i++) st[i][len]=st[st[i][len-1]][len-1];
	}
	int x,y;
	for(int i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		int tmp=lca(x,y);
		printf("%d\n",in[x]+in[y]-in[tmp]-in[st[tmp][0]]);
	}
	return 0;
}

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值