kruskal重构树就是在进行kruskal算法时将边权改成点权,构造出一棵具有 2 n − 1 2n - 1 2n−1 个点的树形结构。
1.在kruskal重构树上,任意两点之间的边权最大值是它们的 l c a lca lca 的点权。
2.除了叶子节点以外,其余节点都代表了一条边的信息。
3.所有非叶子节点自下而上点权单调不降。
kruskal重构树主要可以用于维护 图上在满足经过的边的某一信息不超过 x x x, 任意节点所能到达的其他所有节点的信息。我们用一个事例进行分析:
分析:
在这道题中,我们可以想到一种很朴素的算法: 在水位线的限制下,看从起点开始,能骑车到达多少个不同节点,并分别枚举这些点,从这些点开始步行。答案就是所有这些点的最短路长度的最小值。 那么问题可以变成 在满足经过边的海拔大于水位线的情况下,查询某一节点所能到达其它节点的信息的极值。可以考虑用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;
}