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;
    }
    
  • 相关阅读:
    POJ 1611 The Suspects
    POJ 2001 Shortest Prefixes(字典树)
    HDU 1251 统计难题(字典树 裸题 链表做法)
    G++ C++之区别
    PAT 乙级 1013. 数素数 (20)
    PAT 乙级 1012. 数字分类 (20)
    PAT 乙级 1009. 说反话 (20)
    PAT 乙级 1008. 数组元素循环右移问题 (20)
    HDU 6063 17多校3 RXD and math(暴力打表题)
    HDU 6066 17多校3 RXD's date(超水题)
  • 原文地址:https://www.cnblogs.com/little-uu/p/14961808.html
Copyright © 2011-2022 走看看