Kruskal 重构树 学习笔记

怨念终结。

前置芝士:倍增,Kruskal,LCA。

概念

问两点之间路径最长的最短/最短的最长是多少。
我们首先能想到 Kruskal 最小生成树,因为它是按照边的大小,从小往大建成一棵树的。
因为边权并不好维护,所以我们将它转换成点权。
在跑 Kruskal 的时候,对于每一条连接两个连通块的边,我们新建一个节点,连接两个连通块,并成为这个合并后的连通块在并查集中的 fa,即之后连边从这里开始。
所以最终两点的 LCA 的点权便是我们所求的最最值。

没看懂?不妨看看下面这张图,其中圆圈圈起来的是节点及其编号,圆圈外面的数字是点权。图中求的是两点路径最长的最小值:

image

例题

P4768 [NOI2018] 归程

首先我们看到,题目要求海拔都高于 \(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;
}
posted @ 2025-02-22 10:31  coding_goat_qwq  阅读(32)  评论(0)    收藏  举报