数据结构——ST表

定义

倍增查询表

作用

区间查询,单点多层次查询,求LCA

原理

倍增思想,能够快速向上推进查询。

模板

void init()
{
	for(int j = 1; j < 20; j++)
		for(int i = 1; i <= n; i++)
			st[i][j] = st[st[i][j-1]][j-1];
}

int query(int a, int b)      // lca模板
{
	if(dep[a] < dep[b]) swap(a, b);
	for(int i = 19; i >= 0; i--)
		if(dep[a] - (1<<i) >= dep[b]) a = st[a][i];
	for(int i = 19; i >= 0; i--)
		if(st[a][i] != st[b][i]) a = st[a][i], b = st[b][i];
	if(a != b) a = st[a][0];
	return a;
}

例题

BZOJ-1787 Meet 紧急集合
Description
欢乐岛上有个非常好玩的游戏,叫做“紧急集合”。在岛上分散有N个等待点,有N-1条道路连接着它们,每一条道路都连接某两个等待点,且通过这些道路可以走遍所有的等待点,通过道路从一个点到另一个点要花费一个游戏币。

参加游戏的人三人一组,开始的时候,所有人员均任意分散在各个等待点上(每个点同时允许多个人等待),每个人均带有足够多的游戏币(用于支付使用道路的花费)、地图(标明等待点之间道路连接的情况)以及对话机(用于和同组的成员联系)。当集合号吹响后,每组成员之间迅速联系,了解到自己组所有成员所在的等待点后,迅速在N个等待点中确定一个集结点,组内所有成员将在该集合点集合,集合所用花费最少的组将是游戏的赢家。

小可可和他的朋友邀请你一起参加这个游戏,由你来选择集合点,聪明的你能够完成这个任务,帮助小可可赢得游戏吗?
Input
第一行两个正整数N和M(N<=500000,M<=500000),之间用一个空格隔开。分别表示等待点的个数(等待点也从1到N进行编号)和获奖所需要完成集合的次数。 随后有N-1行,每行用两个正整数A和B,之间用一个空格隔开,表示编号为A和编号为B的等待点之间有一条路。 接着还有M行,每行用三个正整数表示某次集合前小可可、小可可的朋友以及你所在等待点的编号。
Output
一共有M行,每行两个数P,C,用一个空格隔开。其中第i行表示第i次集合点选择在编号为P的等待点,集合总共的花费是C个游戏币。
Sample Input
6 4
1 2
2 3
2 4
4 5
5 6
4 5 6
6 3 1
2 4 4
6 6 6

Sample Output

5 2 
2 5 
4 1 
6 0 

思路

其实就是LCA裸题啦,先求两个点的LCA再与第三个点求一次LCA,然后在三种取法里选一个最小的。

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
using namespace std;

const int maxn = 5e5 + 10;
int st[maxn][25];
int u[maxn*2], v[maxn*2], to[maxn*2], sn[maxn], dex = 0, dep[maxn];
int m, n;

inline int read()
{
    char ch = getchar(); int x = 0, f = 1;
    while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
    while('0' <= ch && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    return x * f;
}

void addedge(int a, int b)
{
	u[++dex] = a, v[dex] = b;
	to[dex] = sn[a];
	sn[a] = dex;
}

void bfs(int a, int fa, int dpt)
{
	dep[a] = dpt;
	int lo = sn[a];
	while(lo){
		int j = v[lo];
		if(j != fa){
			st[j][0] = a;
			bfs(j, a, dpt+1);
		}
		lo = to[lo];
	}
}

void init()
{
	for(int j = 1; j < 20; j++)
		for(int i = 1; i <= n; i++)
			st[i][j] = st[st[i][j-1]][j-1];
}

int query(int a, int b)
{
	if(dep[a] < dep[b]) swap(a, b);
	for(int i = 19; i >= 0; i--)
		if(dep[a] - (1<<i) >= dep[b]) a = st[a][i];
	for(int i = 19; i >= 0; i--)
		if(st[a][i] != st[b][i]) a = st[a][i], b = st[b][i];
	if(a != b) a = st[a][0];
	return a;
}

int main()
{
	memset(to, 0, sizeof(to));
	memset(st, 0, sizeof(st));
	memset(sn, 0, sizeof(sn));
	int a, b, c, d;
	n = read(), m = read();
	for(int i = 1; i < n; i++)
		a = read(), b = read(),
		addedge(a, b), addedge(b, a);
	bfs(1, 1, 1); init();
	for(int i = 1; i <= m; i++){
		a = read(), b = read(), c = read();
		int x, res, tmp;
		d = query(a, b);
		res = dep[a] + dep[b] - dep[d]*2;
		res += dep[d] + dep[c] - dep[query(d, c)]*2;
		x = d;
		
		d = query(b, c);
		tmp = dep[b] + dep[c] - dep[d]*2;
		tmp += dep[d] + dep[a] - dep[query(d, a)]*2;
		if(tmp < res) x = d, res = tmp;
		
		d = query(a, c);
		tmp = dep[a] + dep[c] - dep[d]*2;
		tmp += dep[d] + dep[b] - dep[query(b, d)]*2;
		if(tmp < res) x = d, res = tmp;
		printf("%d %d\n", x, res);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值