Kruskal 重构树 学习笔记
怨念终结。
前置芝士:倍增,Kruskal,LCA。
概念
问两点之间路径最长的最短/最短的最长是多少。
我们首先能想到 Kruskal 最小生成树,因为它是按照边的大小,从小往大建成一棵树的。
因为边权并不好维护,所以我们将它转换成点权。
在跑 Kruskal 的时候,对于每一条连接两个连通块的边,我们新建一个节点,连接两个连通块,并成为这个合并后的连通块在并查集中的 fa,即之后连边从这里开始。
所以最终两点的 LCA 的点权便是我们所求的最最值。
没看懂?不妨看看下面这张图,其中圆圈圈起来的是节点及其编号,圆圈外面的数字是点权。图中求的是两点路径最长的最小值:

例题
首先我们看到,题目要求海拔都高于 \(p\),那么我们的最优策略便是开车直到海拔不高于 \(p\),此时下车走路。
那么我们用 Kruskal 重构树跑出第 \(i\) 号点到其他点的最最值 \(val\)(此处应为最大的最小值)。此时 \(i\) 号点的子树中的叶子节点都可以在水位线小于 \(val\) 的时候开车互相抵达,所以答案就是 \(v\) 的最浅的点权大于 \(p\) 的父亲节点的子树中所有叶子节点到 \(1\) 距离的最小值。
点击查看代码
#include<bits/stdc++.h>
#define int ll
#define ll long long
#define i128 __int128
#define mem(a,b) memset((a),(b),sizeof(a))
#define m0(a) memset((a),0,sizeof(a))
#define m1(a) memset(a,-1,sizeof(a))
#define lb(x) ((x)&-(x))
#define lc(x) ((x)<<1)
#define rc(x) (((x)<<1)|1)
#define pb(G,x) (G).push_back((x))
#define For(a,b,c) for(int a=(b);a<=(c);a++)
#define Rep(a,b,c) for(int a=(b);a>=(c);a--)
#define in1(a) a=read()
#define in2(a,b) a=read(), b=read()
#define in3(a,b,c) a=read(), b=read(), c=read()
#define fst first
#define scd second
#define dbg puts("IAKIOI")
using namespace std;
int read() {
int x=0,f=1; char c=getchar();
for(;c<'0'||c>'9';c=getchar()) f=(c=='-'?-1:1);
for(;c<='9'&&c>='0';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
return x*f;
}
void write(int x) { if(x>=10) write(x/10); putchar('0'+x%10); }
const int mod = 998244353;
int qpo(int a,int b) {int res=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1) res=res*a%mod; return res; }
int inv(int a) {return qpo(a,mod-2); }
#define maxn 400050
int n,m;
int Q,K,S;
struct edge{
int u,v,l,a;
}ed[maxn];
struct DSU{
int fa[maxn];
void pre() {For(i,0,maxn-1) fa[i]=i; }
int fnd(int x) {return fa[x]==x?fa[x]:fa[x]=fnd(fa[x]); }
}dsu;
int cnt=0,val[maxn];
int fa[26][maxn];
vector<pair<int,int> > G[maxn];
bool cmp(edge x,edge y) {
return x.a>y.a;
}
int dis[maxn];
bool vis[maxn];
void dij() {
mem(dis,0x3f);
m0(vis);
dis[1]=0;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > >q;
q.push({0,1});
while(!q.empty()) {
auto [qwq,u]=q.top();
q.pop();
if(vis[u]) continue;
vis[u]=1;
for(auto [v,d]:G[u]) if(dis[u]+d<dis[v]) {
dis[v]=dis[u]+d;
q.push({dis[v],v});
}
}
}
void krusc() {
dsu.pre();
sort(ed+1,ed+m+1,cmp);
cnt=n;
For(i,1,m) {
auto [u,v,qwq,d]=ed[i];
u=dsu.fnd(u),v=dsu.fnd(v);
if(u!=v) {
cnt++;
dsu.fa[u]=dsu.fa[v]=cnt;
val[cnt]=d;
G[cnt].push_back({v,0});
G[cnt].push_back({u,0});
if(cnt==n*2-1) break;
}
}
}
int ans[maxn];
void dfs(int u,int fath) {
fa[0][u]=fath,ans[u]=1e18;
bool flg=0;
for(auto [v,qwq]:G[u]) if(v!=fath)
dfs(v,u),ans[u]=min(ans[u],ans[v]),flg=1;
if(!flg) ans[u]=dis[u];
}
void pre_fa() {
For(i,1,25) For(j,1,cnt)
fa[i][j]=fa[i-1][fa[i-1][j]];
}
int fnd_fa(int x,int p) {
Rep(i,25,0) if(val[fa[i][x]]>p) x=fa[i][x];
return x;
}
void work() {
m0(fa); m0(val);
in2(n,m);
For(i,1,m) {
in2(ed[i].u,ed[i].v);
in2(ed[i].l,ed[i].a);
G[ed[i].u].push_back({ed[i].v,ed[i].l});
G[ed[i].v].push_back({ed[i].u,ed[i].l});
}
dij();
For(i,1,n+n) G[i].clear();
krusc();
dfs(cnt,0);
pre_fa();
in3(Q,K,S);
int lstans=0;
while(Q-- ){
int v,p;
in2(v,p);
v=(v+K*lstans-1)%n+1;
p=(p+K*lstans)%(S+1);
lstans=ans[fnd_fa(v,p)];
cout<<lstans<<'\n';
}
For(i,1,n+n) G[i].clear();
}
signed main() {
// freopen("data.in","r",stdin);
// freopen("myans.out","w",stdout);
// ios::sync_with_stdio(false);
// cin.tie(0); cout.tie(0);
double stt=clock();
int _=1;
_=read();
// cin>>_;
For(i,1,_) {
work();
}
cerr<<"\nTotal Time is:"<<(clock()-stt)*1.0/1000<<" second(s)."<<'\n';
return 0;
}
本文来自博客园,作者:coding_goat_qwq,转载请注明原文链接:https://www.cnblogs.com/CodingGoat/p/18730645

浙公网安备 33010602011771号