「泉州一中基地赛20180519」第四题题解 (代码注释有时间再补)

   日期:2020-11-08     浏览:86    评论:0    
核心提示:1.题目2.分析①操作一应该做的操作我们可以知道,树上两点的路径 (路径上的点的集合记作l)(路径上的点的集合记作l)(路径上的点的集合记作l) 是确定的,所以任意两个被标记的点之间的路径上的点 (记作x)(记作x)(记作x) 的 f(x)f(x)f(x),满足下面的等式f(i)=f(j)(i∈l)f(i) = f(j) (i \in l)f(i)=f(j)(i∈l)解释:这个等式相当于从iii走到jjj后再前往任意一个被标记的点(但不走回头路)所以我们可以在 lll 集合选一个标兵(并查集的树

1.题目

2.分析

①操作一应该做的操作

我们可以知道,树上两点的路径 ( 路 径 上 的 点 的 集 合 记 作 l ) (路径上的点的集合记作l) (l) 是确定的,所以任意两个被标记的点之间的路径上的点 ( 记 作 x ) (记作x) (x) f ( x ) f(x) f(x),满足下面的等式

f ( i ) = f ( j ) ( i ∈ l ) f(i) = f(j) (i \in l) f(i)=f(j)(il

解释:这个等式相当于从 i i i走到 j j j后再前往任意一个被标记的点(但不走回头路)

所以我们可以在 l l l 集合选一个标兵(并查集的树根),用 T a Ta Ta 来表示整个并查集(因为整个并查集的f(x)相等)

参考代码

void UnionSet (int x, int y) { 
	int u = FindSet (x), v = FindSet (y);
	if (u == v) return;
	fa[u] = v;
	_min[v] = Min (_min[u], _min[v]);
}

bool dfs (int u, int father) { 
	if (check (start, u)) return 1;
	for (int i = head[u]; i; i = e[i].next) { 
		int v = e[i].to;
		if (v == father) continue;
		bool flag = dfs (v, u);
		if (flag == 1) { 
			vis[u] = 1;
			UnionSet (start, u);
			return 1;
		}
	}
	return 0;
}

②操作二应该做的操作

将并查集记作 s s s

对于每个点的 f ( x ) f(x) f(x) ,我们可以得出

f ( x ) = M i n ( ( f ( j ) , d p ( x , j ) ) ( j ∈ s ) f(x) = Min ((f(j), dp(x, j))(j \in s) f(x)=Min((f(j),dp(x,j))(js)

( d p ( x , j ) 表 示 x 走 到 j 的 路 径 上 的 点 的 最 小 值 ) (dp(x,j)表示x走到j的路径上的点的最小值) (dp(x,j)xj)

d i s ( x , j ) dis(x, j) dis(x,j)时我们可以用一个类似于并查集路径压缩的思想,得出下列转移式

d p ( x , j ) = M i n ( x , d p ( y , j ) ) ( y 是 x 的 子 节 点 ) dp(x, j) = Min (x, dp(y, j)) (y是x的子节点) dp(x,j)=Min(x,dp(y,j))(yx)

同时,我们搜到一个点 j ( j ∈ s ) j(j \in s) j(js) 时,还要保存这个点的编号 (去找 f ( j ) ∈ s f(j) \in s f(j)s)

同样可以用一个类似于并查集路径压缩的思想,得出下列转移式

i n d e x [ x ] = i n d e x [ y ] ( y 是 x 的 子 节 点 ) index[x] = index[y](y是x的子节点) index[x]=index[y](yx)

i n d e x [ i ] 指 向 一 个 在 并 查 集 内 的 一 个 节 点 的 下 标 index[i]指向一个在并查集内的一个节点的下标 index[i]

参考代码

int find_root (int u, int father)  { 
	if (check (u, start)) { 
		return u;
	}
	for (int i = head[u]; i; i = e[i].next) { 
		int v = e[i].to;
		if (v == father) continue;
		int flag = find_root (v, u);
		if (flag == -1) continue;
		index[u] = flag;
		dp[u] = Min (u, dp[v]);
		return flag;
	}
	return -1;
}

参考代码 (综上所述)

#include <cstdio> 
#include <iostream>
using namespace std;

const int MAXN = 1e5 + 5;
const int INF = 0x3f3f3f3f;

int n, q, start = -1;
int fa[MAXN], _min[MAXN], index[MAXN], dp[MAXN];
//dp表示走到并查集的路径中的最小值 
bool vis[MAXN];
//vis[i] == 1表示i在并查集里面 

int len, head[MAXN];
struct edge { 
	int to, next;
}e[MAXN * 2];

int Min (int x, int y) {  return x < y ? x : y; }

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

void MakeSet () { 
	for (int i = 1; i <= n; i++) { 
		fa[i] = i;
		_min[i] = i;
		index[i] = i;
		dp[i] = INF;
	}
}

int FindSet (int x) { 
	if (fa[x] != x) { 
		fa[x] = FindSet (fa[x]);
	}
	return fa[x];
}

void UnionSet (int x, int y) { 
	int u = FindSet (x), v = FindSet (y);
	if (u == v) return;
	fa[u] = v;
	_min[v] = Min (_min[u], _min[v]);
}

bool check (int x, int y) { 
	int u = FindSet (x), v = FindSet (y);
	if (u == v) return 1;
	else return 0;
}

bool dfs (int u, int father) { 
	if (check (start, u)) return 1;
	for (int i = head[u]; i; i = e[i].next) { 
		int v = e[i].to;
		if (v == father) continue;
		bool flag = dfs (v, u);
		if (flag == 1) { 
			vis[u] = 1;
			UnionSet (start, u);
			return 1;
		}
	}
	return 0;
}

int find_root (int u, int father)  { 
	if (check (u, start)) { 
		return u;
	}
	for (int i = head[u]; i; i = e[i].next) { 
		int v = e[i].to;
		if (v == father) continue;
		int flag = find_root (v, u);
		if (flag == -1) continue;
		index[u] = flag;
		dp[u] = Min (u, dp[v]);
		return flag;
	}
	return -1;
}

void add (int x, int y) { 
	e[++len].to = y;
	e[len].next = head[x];
	head[x] = len;
}

int main () { 
	n = read (); q = read ();
	MakeSet ();
	for (int i = 1; i < n; i++) { 
		int x, y; x = read (); y = read ();
		add (x, y); add (y, x);
	}
	for (int i = 1; i <= q; i++) { 
		int type, x; type = read (); x = read ();
		if (type == 1) { 
			if (start == -1) { 
				start = x;
				continue;
			}
			dfs (x, -1);
		}
		else { 
			if (vis[x] == 0) { //不在并查集内 
				if (dp[x] == INF) { 
					find_root (x, -1);
				}
				printf ("%d\n", Min (dp[x], _min[FindSet (index[x])]));
			}
			else { //在并查集内 
				printf ("%d\n", _min[FindSet (x)]);
			}
		}
// for (int j = 1; j <= n; j++) printf ("root[%d] = %d, _min = %d\n", j, FindSet (j), _min[FindSet (j)]);
	}
	return 0;
}
 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服