kruskal重构树

一.kruskal重构树是什么

        kruskal重构树就是在进行kruskal算法时将边权改成点权,构造出一棵具有 2 n − 1 2n - 1 2n1 个点的树形结构。

二.kruskal重构树的性质

        1.在kruskal重构树上,任意两点之间的边权最大值是它们的 l c a lca lca 的点权。
        2.除了叶子节点以外,其余节点都代表了一条边的信息。
       3.所有非叶子节点自下而上点权单调不降。

三.kruskal重构树的用途

        kruskal重构树主要可以用于维护 图上在满足经过的边的某一信息不超过 x x x, 任意节点所能到达的其他所有节点的信息。我们用一个事例进行分析:

[NOI2018] 归程

分析:
        在这道题中,我们可以想到一种很朴素的算法: 在水位线的限制下,看从起点开始,能骑车到达多少个不同节点,并分别枚举这些点,从这些点开始步行。答案就是所有这些点的最短路长度的最小值。 那么问题可以变成 在满足经过边的海拔大于水位线的情况下,查询某一节点所能到达其它节点的信息的极值。可以考虑用kruskal重构树求解。具体来讲,我们设计以下算法:
       1. 以1为起点,跑最短路。
        2.将边 按海拔降序排序, 并使用kruskal重构树算法建图。同时转移以每一个点为根,它所在子树中的最短路的最小值。
        3.对于每一次查询,倍增求出最后一个点权(海拔)大于水位线的父亲,答案就是 2 中维护的信息。

        因此我们可以得知: 按某一属性将边升序/降序排序,使用kruskal重构树算法后,对于每一个点, 在满足 图上经过的所有边的该属性不超过/不大于 x x x 这一条件的情况下, 所能到达的点就是最上方祖先所在子树的所有叶子节点。

CODE:

#include//kruskal重构树 + 最短路 
using namespace std;
const int M = 4e5 + 10;
const int N = 2e5 + 10;
int read(){
	int x = 0, f = 1; char c = getchar();
	while(!isdigit(c)){if(c == '-') f = -1; c = getchar();}
	while(isdigit(c)){x = (x << 1) + (x << 3) + (c ^ 48); c = getchar();}
	return x * f;
}
int n, m, T, Q, k, s, p, bin[M], cnt, H[M], Minx[M], head[M], tot, fa[M][25], dis[M], v, ans;
int INF = 2e9 + 10;
vector< int >son[M];
struct edge{
	int u, v, len, height;
}E[M];
struct e{
	int v, last, lenth;
}G[M * 2];
bool cmp(edge a, edge b){
	return a.height > b.height;
}
void Add(int u, int v, int w){
	G[++tot].v = v;
	G[tot].last = head[u];
	G[tot].lenth = w;
	head[u] = tot;
}
struct state{
	int x, w;
	bool operator < (const state &a)const{
		return a.w < w;
	}
};
priority_queue< state > q; 
bool vis[N];
void dijkstra(int st){
	memset(dis, 0x3f, sizeof(dis));
	dis[st] = 0;
	memset(vis, 0, sizeof(vis));
	q.push(state{st, dis[st]});
	while(!q.empty()){
		state t = q.top(); q.pop();
		int x = t.x, L = t.w;
		if(vis[x]) continue;
		vis[x] = 1;
		for(int i = head[x]; i; i = G[i].last){
			int v = G[i].v;
			int w = G[i].lenth;
			if(dis[v] > dis[x] + w){
				dis[v] = dis[x] + w;
				q.push(state{v, dis[v]});
			}
		}
	}
}
void add(int x, int y){
	son[x].push_back(y);
}

void dfs(int x, int fat){//转移
	fa[x][0] = fat;
	for(int i = 1; i <= 20; i++) fa[x][i] = fa[fa[x][i - 1]][i - 1];
    for(int i = 0; i < son[x].size(); i++){
    	int v = son[x][i];
    	dfs(v, x);
    	Minx[x] = min(Minx[v], Minx[x]);
	}	
	Minx[x] = min(Minx[x], dis[x]);
}
int Find(int x){
	return bin[x] == x ? x : bin[x] = Find(bin[x]);
}
int ask(int id, int Max_h){//下标, 水位线 找到最后一个高度>Max_h的祖先 
	int x = id;
	for(int i = 20; i >= 0; i--){//倍增
		if(H[fa[x][i]] > Max_h) x = fa[x][i];
	}
	return Minx[x];
}
int main(){
	T = read();
	while(T--){
		n = read(), m = read();
		cnt = n; tot = 0;
		for(int i = 1; i <= n * 2; i++) bin[i] = i, son[i].clear(), Minx[i] = INF, head[i] = 0;
		for(int i = 1; i <= m; i++){
			E[i].u = read(), E[i].v = read(), E[i].len = read(), E[i].height = read();
			Add(E[i].u, E[i].v, E[i].len);
			Add(E[i].v, E[i].u, E[i].len);
		}
		dijkstra(1);
		sort(E + 1, E + m + 1, cmp);
		for(int i = 1; i <= m; i++){
			int u = E[i].u, v = E[i].v;
			if(Find(u) == Find(v)) continue;
			int f1 = Find(u), f2 = Find(v);
			H[++cnt] = E[i].height;//存非叶子节点的信息
			add(cnt, f1), add(cnt, f2);//建树
			bin[f1] = bin[cnt];
			bin[f2] = bin[cnt];
		}
		dfs(cnt, 0);
		Q = read(), k = read(), s = read();
		ans = 0;
		for(int i = 1; i <= Q; i++){
			v = read(), p = read();
			v= (v + k * ans - 1) % n + 1;
			p = (p + k * ans) % (s + 1); 
			ans = ask(v, p);
			printf("%d\n", ans);
		}
	}
	return 0;
} 

你可能感兴趣的:(重构,算法,图论)