Kruskal 重构树

多用于在线解决:在一个图中只能走 \(\le/\ge/</> l\) 的边,从 \(u\) 出发,所能到达的点,针对它们进行的一系列询问。

Kruskal 重构树是基于 Kruskal MST 算法生成的一棵含有 \(n+m\) 个节点的点带权二叉树,有 \(n\) 个叶子,分别对应原 \(n\) 个节点;它可以让从 \(u\) 出发,只经过 \(\le l\) 的边的所有点集中在某个子树的所有叶子上,进而使用线段树等数据结构方便维护。

流程

初始时重构树中有 \(n\) 个节点,没有边,每个节点权值为 0。
将所有边从小到大排序,如 Kruskal MST 地遍历,判断两端 \(x,y\) 是否在一个并查集内,不在的话,新建一个节点 \(t\)(从 \(n+1\) 开始编号),将 \(x\) 所在连通块(子树)的根和 \(y\) 所在子树的根分别连向 \(t\),并 fa[find(y)]=find(x),并将合并后的并查集的根重置为 \(t\),将 \(t\) 的点权设为这条边的边权。

性质

父亲的点权大于等于儿子的点权。
两点之间的路径上边权最大值的最小值 = MST 中两点之间树边最大值 = 重构树中两点 lca 的点权
从一个点出发只经过小于等于某值的边能到达的所有点处在同一子树的所有叶子上。

针对第三点,如何定位这个子树?借助第一点,在 \(u\) 到根的链上倍增一个最浅的点使得它的点权是 \(\le l\) 的,它就是子树根。

如果是 \(\ge\),排序的时候就从大到小来。

模板:[NOI2018]归程

#include <bits/stdc++.h>
using namespace std;
inline int read(){
	register char ch=getchar();register int x=0;
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	return x;
}
void print(int x){
	if(x/10)print(x/10);
	putchar(x%10+48);
}
const int N=2e5+5,INF=0x3f3f3f3f;
int n,m,q,K,S,nod,dfc,dis[N],fa[N*3][19],ma[N],rep[N*3],dfn[N*3],out[N*3],val[N*3],leaf[N],t[N<<2];
vector<pair<int,int> >G[N];
vector<int>T[N*3];
priority_queue<pair<int,int> >Q;
struct Edge {int u,v,l,a;}e[N*2];
int find(int x){return x==ma[x]?x:ma[x]=find(ma[x]);}
void unite(int x,int y){
	x=find(x),y=find(y);
	nod++;
	T[nod].push_back(rep[x]);
	T[nod].push_back(rep[y]);
	//printf("[%d %d]\n[%d %d]\n",nod,rep[x],nod,rep[y]);
	ma[y]=x;
	rep[x]=nod;
}
void dfs(int x){
	dfn[x]=dfc;
	if(!T[x].size())dfc++,leaf[dfc]=x;
	for(int i=1;i<=18;i++)fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int y:T[x])fa[y][0]=x,dfs(y);
	out[x]=dfc;
}
void pushup(int k){t[k]=min(t[k<<1],t[k<<1|1]);}
void build(int l,int r,int k){
	if(l==r){t[k]=dis[leaf[l]];return;}
	int mid=l+r>>1;
	build(l,mid,k<<1),build(mid+1,r,k<<1|1);
	pushup(k);
}
int ask(int L,int R,int l,int r,int k){
	if(L<=l&&r<=R)return t[k];
	int mid=l+r>>1,s=INF;
	if(L<=mid)s=min(s,ask(L,R,l,mid,k<<1));
	if(R>mid)s=min(s,ask(L,R,mid+1,r,k<<1|1));
	return s;
}
void solve(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)G[i].clear();
	for(int i=1;i<=n+m;i++)T[i].clear();
	dfc=0;
	for(int i=1;i<=m;i++){
		e[i].u=read(),e[i].v=read(),e[i].l=read(),e[i].a=read();
		G[e[i].u].push_back(make_pair(e[i].v,e[i].l));
		G[e[i].v].push_back(make_pair(e[i].u,e[i].l));
	}
	memset(dis,0x3f,sizeof dis),dis[1]=0,Q.push(make_pair(0,1));
	while(!Q.empty()){
		int x=Q.top().second;Q.pop();
		for(pair<int,int>y:G[x]){
			if(dis[y.first]>dis[x]+y.second){
				dis[y.first]=dis[x]+y.second;
				Q.push(make_pair(-dis[y.first],y.first));
			}
		}
	}
	sort(e+1,e+m+1,[](Edge a,Edge b){return a.a>b.a;});
	nod=n;
	for(int i=1;i<=n;i++)ma[i]=rep[i]=i,val[i]=INF;
	for(int i=1;i<=m;i++)if(find(e[i].u)!=find(e[i].v)){
		unite(e[i].u,e[i].v);
		val[nod]=e[i].a;
	}
	dfs(nod);
	build(1,n,1);
	//for(int i=1;i<=n;i++)cout<<dfn[i]<<' '<<out[i]<<'\n';
	q=read(),K=read(),S=read();
	for(int v,p,las=0;q--;){
		v=(read()+K*las-1)%n+1,p=(read()+K*las)%(S+1);
		for(int i=18;~i;i--)if(fa[v][i]&&val[fa[v][i]]>p)v=fa[v][i];
		print(las=ask(dfn[v]+1,out[v],1,n,1)),puts("");
	}
}
int main(){
	int T=read();
	while(T--)solve();
}
posted @ 2022-07-12 22:02  pengyule  阅读(323)  评论(0)    收藏  举报