算法学习————Kruskal重构树
Kruskal重构树:
简单来讲,就是在Kruskal算法进行的过程中,我们把最小生成树的边权改为点权,加虚点连边,原树的节点个数变成2n-1个
Kruskal重构树的性质
1.根据我们构造的过程,这是一个二叉堆
2.原树两点之间的边权最大值是重构树上两点Lca的权值
3.重构树中代表原树中的点的节点全是叶子节点,其余节点都代表了一条边的边权。
Kruskal重构树的构造
-
首先对边排序
-
使用并查集辅助加边,每新建一条边时:新建节点index(编号从n+1开始)
-
将原有两节点所在集合改为index
-
将原有节点与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;
}