zoukankan      html  css  js  c++  java
  • 算法学习————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
    ",lst);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    CSS实现返回网页顶部
    jQuery实现小火箭动态返回顶部代码
    Linux目录结构介绍
    Linux常用命令及技巧
    Linux文件系统
    Linux特性
    numpy中基础函数
    restful规范
    堆栈
    三次握手与四次挥手
  • 原文地址:https://www.cnblogs.com/little-uu/p/14961808.html
Copyright © 2011-2022 走看看