kruskal重構樹學習筆記

liuchanglc發表於2021-01-20

內容

按照 \(kruskal\) 演算法的流程,把最小/大生成樹中邊權的關係對映到了一顆二叉樹上

具體實現也很簡單

在原本的 \(kruskal\) 演算法中,每次查到兩個不在同一集合的點,就新開一個節點

然後把兩個節點的祖先節點分別向新節點連邊,不計邊權,但是要記錄新點的點權,就是連線兩個點的邊的邊權

新生成的樹有以下特點

\(1\)、這棵樹是一棵二叉樹,且具有堆的性質。

\(2\)、樹上除了葉子節點是原來的點,其餘的點都是新建的點,且都有權值。

\(3\)、兩個點 \(u\)\(v\)\(lca\) 的點權就對應著它們最小生成樹上的路徑上的最小/大值(瓶頸)

這樣,我們就可以解決從某一個點出發,只能經過邊權小於/大於 \(x\) 的邊,在所有能到達的點中查詢最值的問題

因為每遇到一個生成樹上的邊我們就會新開一個節點,所以節點數變為了原圖的 \(2\)

P4768 [NOI2018] 歸程

題目傳送門

分析

可以說是 \(kruskal\) 重構樹的板子題

首先按照邊權從大到小排序,構建 \(kruskal\) 重構樹

這樣,重構樹就是一個小根堆

因為每次詢問時海拔大於 \(a\) 的邊都可以乘車通過

所以我們肯定要在 \(v\) 能乘車到達的點中選擇到 \(1\) 的最短路最短的點

在重構樹上,我們只需要維護一個倍增陣列

查詢時倍增跳到第一個深度小於等於 \(a\) 的祖先節點

這個節點的子樹中的所有點一定可以由 \(x\) 乘車到達

只要在這棵子樹中查詢到 \(1\) 的最短路的最小值就行了

程式碼

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=1e6+5;
int h[maxn],tot=1,n,m,q,k,s,t,dis[maxn],cnt,fa[maxn],val[maxn];
struct Node{
	int zb,yb,val;
	Node(){}
	Node(rg int aa,rg int bb,rg int cc){
		zb=aa,yb=bb,val=cc;
	}
}jl[maxn];
bool cmp(rg Node aa,rg Node bb){
	return aa.val>bb.val;
}
int zhao(rg int xx){
	if(xx==fa[xx]) return xx;
	return fa[xx]=zhao(fa[xx]);
}
struct asd{
	int to,nxt,val;
}b[maxn];
void ad(rg int aa,rg int bb,rg int cc){
	b[tot].to=bb;
	b[tot].nxt=h[aa];
	b[tot].val=cc;
	h[aa]=tot++;
}
bool vis[maxn];
struct jie{
	int num,jl;
	jie(){}
	jie(rg int aa,rg int bb){
		num=aa,jl=bb;
	}
	bool operator <(const jie& A)const{
		return jl>A.jl;
	}
};
void dij(){
	std::priority_queue<jie> Q;
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	dis[1]=0;
	Q.push(jie(1,0));
	while(!Q.empty()){
		rg int now=Q.top().num;
		Q.pop();
		if(vis[now]) continue;
		vis[now]=1;
		for(rg int i=h[now];i!=-1;i=b[i].nxt){
			rg int u=b[i].to;
			if(dis[u]>dis[now]+b[i].val){
				dis[u]=dis[now]+b[i].val;
				Q.push(jie(u,dis[u]));
			}
		}
	}
}
int dep[maxn],mindis[maxn],zx[maxn][22],mmin[maxn][22];
std::vector<int> g[maxn];
void dfs(rg int now,rg int lat){
	dep[now]=dep[lat]+1;
	mindis[now]=dis[now];
	mmin[now][0]=val[lat];
	zx[now][0]=lat;
	for(rg int i=1;(1<<i)<=dep[now];i++){
		zx[now][i]=zx[zx[now][i-1]][i-1];
		mmin[now][i]=std::min(mmin[now][i-1],mmin[zx[now][i-1]][i-1]);
	}
	for(rg int i=0;i<g[now].size();i++){
		rg int u=g[now][i];
		dfs(u,now);
		mindis[now]=std::min(mindis[now],mindis[u]);
	}
}
int latans;
int solve(rg int now,rg int val){
	for(rg int i=20;i>=0;i--){
		if(mmin[now][i]>val){
			now=zx[now][i];
		}
	}
	return mindis[now];
}
int main(){
	t=read();
	while(t--){
		memset(dep,0,sizeof(dep));
		memset(h,-1,sizeof(h));
		memset(mmin,0,sizeof(mmin));
		memset(zx,0,sizeof(zx));
		tot=1,latans=0;
		n=read(),m=read();
		rg int aa,bb,cc,dd;
		for(rg int i=1;i<=m;i++){
			aa=read(),bb=read(),cc=read(),dd=read();
			jl[i]=Node(aa,bb,dd);
			ad(aa,bb,cc),ad(bb,aa,cc);
		}	
		dij();
		cnt=n;
		std::sort(jl+1,jl+m+1,cmp);
		for(rg int i=1;i<=n+n;i++) fa[i]=i;
		for(rg int i=1;i<=n+n;i++) g[i].clear();
		for(rg int i=1;i<=m;i++){
			aa=jl[i].zb,bb=jl[i].yb,cc=jl[i].val;
			aa=zhao(aa),bb=zhao(bb);
			if(aa==bb) continue;
			val[++cnt]=cc;
			fa[aa]=fa[bb]=cnt;
			g[cnt].push_back(aa),g[cnt].push_back(bb);
		}
		dfs(cnt,0);
		q=read(),k=read(),s=read();
		for(rg int i=1;i<=q;i++){
			aa=read(),bb=read();
			aa=(aa+k*latans-1)%n+1;
			bb=(bb+k*latans)%(s+1);
			printf("%d\n",latans=solve(aa,bb));
		}
	}
	return 0;
}

P4197 Peaks

題目傳送門

分析

還是 \(kruskal\) 重構樹的板子題

按照邊權從小到大排序構建重構樹

查詢的時候只需要在祖先節點對應的主席樹裡查詢 \(k\) 大就行了

因為只有詢問子樹的操作,所以可以按照 \(dfn\) 序進行處理

程式碼

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
#define rg register
inline int read(){
	rg int x=0,fh=1;
	rg char ch=getchar();
	while(ch<'0' || ch>'9'){
		if(ch=='-') fh=-1;
		ch=getchar();
	}
	while(ch>='0' && ch<='9'){
		x=(x<<1)+(x<<3)+(ch^48);
		ch=getchar();
	}
	return x*fh;
}
const int maxn=4e5+5,maxm=5e5+5;
int h[maxn],tot=1,fa[maxn],n,m,q,val[maxn],cnt,hig[maxn];
struct Node{
	int zb,yb,val;
	Node(){}
	Node(rg int aa,rg int bb,rg int cc){
		zb=aa,yb=bb,val=cc;
	}
}jl[maxm];
bool cmp(rg Node aa,rg Node bb){
	return aa.val<bb.val;
}
struct asd{
	int to,nxt;
}b[maxn<<1];
void ad(rg int aa,rg int bb){
	b[tot].to=bb;
	b[tot].nxt=h[aa];
	h[aa]=tot++;
}
int zhao(rg int xx){
	if(xx==fa[xx]) return xx;
	return fa[xx]=zhao(fa[xx]);
}
int zx[maxn][22],mmax[maxn][22],dep[maxn],rt[maxn],rtcnt;
struct trr{
	int lch,rch,siz;
}tr[maxn*50];
int ad(rg int da,rg int pre,rg int l,rg int r,rg int wz){
	da=++rtcnt;
	tr[da]=tr[pre];
	tr[da].siz++;
	if(l==r) return da;
	rg int mids=(l+r)>>1;
	if(wz<=mids) tr[da].lch=ad(tr[da].lch,tr[pre].lch,l,mids,wz);
	else tr[da].rch=ad(tr[da].rch,tr[pre].rch,mids+1,r,wz);
	return da;
}
int cx(rg int da,rg int pre,rg int l,rg int r,rg int kth){
	if(l==r) return l;
	rg int mids=(l+r)>>1,nsiz=tr[tr[da].rch].siz-tr[tr[pre].rch].siz;
	if(nsiz>=kth) return cx(tr[da].rch,tr[pre].rch,mids+1,r,kth);
	else return cx(tr[da].lch,tr[pre].lch,l,mids,kth-nsiz);
}
int getlca(rg int xx,rg int yy){
	if(dep[xx]<dep[yy]) std::swap(xx,yy);
	rg int len=dep[xx]-dep[yy],k=0;
	while(len){
		if(len&1) xx=zx[xx][k];
		k++,len>>=1;
	}
	if(xx==yy) return xx;
	for(rg int i=20;i>=0;i--){
		if(zx[xx][i]!=zx[yy][i]){
			xx=zx[xx][i],yy=zx[yy][i];
		}
	}
	return zx[xx][0];
}
int dfn[maxn],dfnc,siz[maxn],rk[maxn],sta[maxn],tp;
void dfs(rg int now,rg int lat){
	dep[now]=dep[lat]+1;
	zx[now][0]=lat;
	mmax[now][0]=val[lat];
	dfn[now]=++dfnc;
	rk[dfnc]=now;
	siz[now]=1;
	for(rg int i=1;(1<<i)<=dep[now];i++){
		zx[now][i]=zx[zx[now][i-1]][i-1];
		mmax[now][i]=std::max(mmax[now][i-1],mmax[zx[now][i-1]][i-1]);
	}
	for(rg int i=h[now];i!=-1;i=b[i].nxt){
		rg int u=b[i].to;
		if(u==lat) continue;
		dfs(u,now);
		siz[now]+=siz[u];
	}
}
int zhao(rg int now,rg int val){
	for(rg int i=20;i>=0;i--){
		if(mmax[now][i]<=val && zx[now][i]){
			now=zx[now][i];
		}
	}
	return now;
}
int solve(rg int xx,rg int yy,rg int kth){
	rg int lca=zhao(xx,yy);
	if(tr[rt[dfn[lca]+siz[lca]-1]].siz-tr[rt[dfn[lca]-1]].siz<kth) return -1;
	return sta[cx(rt[dfn[lca]+siz[lca]-1],rt[dfn[lca]-1],1,tp,kth)];
}
int main(){
	memset(mmax,0x3f,sizeof(mmax));
	memset(h,-1,sizeof(h));
	n=read(),m=read(),q=read();
	for(rg int i=1;i<=n;i++) hig[i]=read();
	for(rg int i=1;i<=n;i++) sta[++tp]=hig[i];
	std::sort(sta+1,sta+1+tp);
	tp=std::unique(sta+1,sta+1+tp)-sta-1;
	for(rg int i=1;i<=n;i++) hig[i]=std::lower_bound(sta+1,sta+1+tp,hig[i])-sta;
	cnt=n;
	rg int aa,bb,cc;
	for(rg int i=1;i<=m;i++){
		aa=read(),bb=read(),cc=read();
		jl[i]=Node(aa,bb,cc);
	}
	std::sort(jl+1,jl+1+m,cmp);
	for(rg int i=1;i<=n+n;i++) fa[i]=i;
	for(rg int i=1;i<=m;i++){
		aa=jl[i].zb,bb=jl[i].yb,cc=jl[i].val;
		aa=zhao(aa),bb=zhao(bb);
		if(aa==bb) continue;
		val[++cnt]=cc;
		fa[aa]=cnt,fa[bb]=cnt;
		ad(aa,cnt),ad(cnt,aa);
		ad(bb,cnt),ad(cnt,bb);
	}
	for(rg int i=1;i<=cnt;i++){
		if(fa[i]==i){
			dfs(i,0);
		}
	}
	for(rg int i=1;i<=cnt;i++){
		rt[i]=rt[i-1];
		if(rk[i]<=n){
			rt[i]=ad(rt[i],rt[i],1,tp,hig[rk[i]]);
		}
	}
	for(rg int i=1;i<=q;i++){
		aa=read(),bb=read(),cc=read();
		printf("%d\n",solve(aa,bb,cc));
	}
	return 0;
}

相關文章