算法学习————Kruskal重构树

Kruskal重构树:

简单来讲,就是在Kruskal算法进行的过程中,我们把最小生成树的边权改为点权,加虚点连边,原树的节点个数变成2n-1个

Kruskal重构树的性质

1.根据我们构造的过程,这是一个二叉堆

2.原树两点之间的边权最大值是重构树上两点Lca的权值

3.重构树中代表原树中的点的节点全是叶子节点,其余节点都代表了一条边的边权。

Kruskal重构树的构造

  1. 首先对边排序

  2. 使用并查集辅助加边,每新建一条边时:新建节点index(编号从n+1开始)

  3. 将原有两节点所在集合改为index

  4. 将原有节点与index连边新建节点的权值为当前边的边权

构建的代码:

        for (int i = 1;i <= m;i++){
		int u = g[i].u,v = g[i].v,w = g[i].w;
		int fu = find(u),fv = find(v);
	//	cout<<"u = "<<u<<" "<<v<<" "<<fu<<" "<<fv<<endl;
		if (fu != fv){
			fa[fu] = fa[fv] = ++cnt;
			val[cnt] = w;
			add(cnt,fu),add(cnt,fv);
			if (cnt == 2*n-1) break;
		}
	}

例题:NOI2018 归程

这道题拿到手我们思考,如果有多个路径,我走哪一条,并且我该在什么时候下车呢?

贪心来想对于一条固定的路径来说,肯定是越晚下车越好,那么一个思路就出来了,我求出每条路径的最晚下车点到家的距离,最小的那一个就是我要走的

最短距离我们可以先最短路预处理,但是一条一条路径找这样的复杂度显然不优,那我们接着思考,最晚下车点是哪个点呢?就是第一个比水位线低的点,这个点连出的边是最晚下车点到出发点的最小边

出现了最小边,不难联想到上面Kruskal重构树的性质,边权从大到小排序,建出来的树,两点间的lca就是最小边权

这样我从起点向上跳,跳到的每一个点,都是他与其他某个点间的最小边权并且这个边权是递减的

那么在建完kruskal重构树之后,我们可以处理出以每个点为根到家的最短路(子树中的点间的最大边权显然都不超过根的点权),然后每次从起点倍增向上跳到最后一个大于水位线的就好了

代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std;
int read(){
	int x = 1,a = 0;char ch = getchar();
	while (ch < '0'||ch > '9'){if (ch == '-') x = -1;ch = getchar();}
	while (ch >= '0'&&ch <= '9'){a = a*10+ch-'0';ch = getchar();}
	return x*a;
}
const int maxn = 1e6+10,inf = 1e9+7;
int T,n,m;
struct node{
	int tot = 0;
	int head[maxn],to[maxn],w[maxn],nxt[maxn];
	inline void add(int u,int v,int k){
		to[++tot] = v;
		nxt[tot] = head[u];
		w[tot] = k;
		head[u] = tot;
	}
	void add(int u,int v){
		to[++tot] = v;
		nxt[tot] = head[u];
		head[u] = tot;
	}
}ed1,ed2;
struct ask{
	int u,v,a,id;
}g[maxn],q[maxn];
bool cmp(ask x,ask y){return x.a > y.a;}
int dis[maxn];
bool vis[maxn];
inline void Dij(){
	priority_queue<pair<int,int> > q;
	for (int i = 1;i <= n;i++) dis[i] = inf,vis[i] = 0;
	dis[1] = 0;
	q.push(make_pair(0,1));
	while (!q.empty()){
		int x = q.top().second;q.pop();
		if (vis[x]) continue;
		vis[x] = 1;
		for (int i = ed1.head[x];i;i = ed1.nxt[i]){
			int to = ed1.to[i];
			if (dis[to] > dis[x]+ed1.w[i]){
				dis[to] = dis[x]+ed1.w[i];
				q.push(make_pair(-dis[to],to));
			}
		}
	} 
}
int fa[maxn],val[maxn],road[maxn];
inline void init(){
	for (int i = 1;i <= 2*n;i++) fa[i] = i,ed1.head[i] = ed2.head[i] = 0;
	ed1.tot = ed2.tot = 0;
	for (int i = n+1;i <= 2*n-1;i++) road[i] = inf;
}
int find(int x){return (x == fa[x]) ? x : fa[x] = find(fa[x]);}
int Q,k,s;
int dep[maxn],f[maxn][30];
void dfs(int x){
	dep[x] = dep[f[x][0]]+1;
	for (int i = 1;i <= 22;i++) f[x][i] = f[f[x][i-1]][i-1];
	for (int i = ed2.head[x];i;i = ed2.nxt[i]){
		int to = ed2.to[i];
		if (to == f[x][0]) continue;
		f[to][0] = x;
		dfs(to);
		road[x] = min(road[x],road[to]);
	}
}
int query(int x,int y){
	for (int i = 22;i >= 0;i--) if (dep[x]-(1 << i) > 0&&val[f[x][i]] > y) x = f[x][i];
	return road[x];
}
int main(){
	freopen("return.in","r",stdin);
	freopen("return.out","w",stdout);
//	freopen("in.in","r",stdin);
//	freopen("out.out","w",stdout);
	T = read();
	while (T--){
		n = read(),m = read();int lst = 0;
		init();
		for (int i = 1;i <= m;i++){
			int u = read(),v = read(),l = read(),a = read();
			ed1.add(u,v,l),ed1.add(v,u,l);
			g[i].u = u,g[i].v = v,g[i].a = a;
		}
		Dij();
		for (int i = 1;i <= n;i++) road[i] = dis[i];
		sort(g+1,g+m+1,cmp); 
//		for (int i = 1;i <= m;i++) cout<<"g = "<<g[i].u<<" "<<g[i].v<<" "<<g[i].a<<endl;
		Q = read(),k = read(),s = read();
		int cnt = n;
		for (int i = 1;i <= m;i++){
			int u = g[i].u,v = g[i].v,a = g[i].a;
			int fu = find(u),fv = find(v);
//			cout<<"fu = "<<fu<<" "<<fv<<endl;
			if (fu != fv){
				val[++cnt] = a;
				fa[fu] = cnt,fa[fv] = cnt;
				ed2.add(cnt,fu),ed2.add(cnt,fv);
			}
//			cout<<"cnt = "<<cnt<<endl;
			if (cnt == 2*n-1) break;
		}
//		cout<<"111111111111111"<<endl;
		dfs(cnt);
		while (Q--){
			int v = read(),p = read();
			v = (v+lst*k-1) % n+1,p = (p+lst*k) % (s+1);
			lst = query(v,p);
			printf("%d\n",lst);
		}
	}
	return 0;
}
posted @ 2021-07-02 09:10  小又又yyyy  阅读(79)  评论(0编辑  收藏  举报