kruskal重構樹學習筆記
阿新 • • 發佈:2021-01-20
## 內容
按照 $kruskal$ 演算法的流程,把最小/大生成樹中邊權的關係對映到了一顆二叉樹上
具體實現也很簡單
在原本的 $kruskal$ 演算法中,每次查到兩個不在同一集合的點,就新開一個節點
然後把兩個節點的祖先節點分別向新節點連邊,不計邊權,但是要記錄新點的點權,就是連線兩個點的邊的邊權
新生成的樹有以下特點
$1$、這棵樹是一棵二叉樹,且具有堆的性質。
$2$、樹上除了葉子節點是原來的點,其餘的點都是新建的點,且都有權值。
$3$、兩個點 $u$ 和 $v$ 的 $lca$ 的點權就對應著它們最小生成樹上的路徑上的最小/大值(瓶頸)
這樣,我們就可以解決從某一個點出發,只能經過邊權小於/大於 $x$ 的邊,在所有能到達的點中查詢最值的問題
因為每遇到一個生成樹上的邊我們就會新開一個節點,所以節點數變為了原圖的 $2$ 倍
## P4768 [NOI2018] 歸程
[題目傳送門](https://www.luogu.com.cn/problem/P4768)
### 分析
可以說是 $kruskal$ 重構樹的板子題
首先按照邊權從大到小排序,構建 $kruskal$ 重構樹
這樣,重構樹就是一個小根堆
因為每次詢問時海拔大於 $a$ 的邊都可以乘車通過
所以我們肯定要在 $v$ 能乘車到達的點中選擇到 $1$ 的最短路最短的點
在重構樹上,我們只需要維護一個倍增陣列
查詢時倍增跳到第一個深度小於等於 $a$ 的祖先節點
這個節點的子樹中的所有點一定可以由 $x$ 乘車到達
只要在這棵子樹中查詢到 $1$ 的最短路的最小值就行了
### 程式碼
``` cpp
#include
#include
#include
#include
#include
#include
#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 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 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<=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
[題目傳送門](https://www.luogu.com.cn/problem/P4197)
### 分析
還是 $kruskal$ 重構樹的板子題
按照邊權從小到大排序構建重構樹
查詢的時候只需要在祖先節點對應的主席樹裡查詢 $k$ 大就行了
因為只有詢問子樹的操作,所以可以按照 $dfn$ 序進行處理
### 程式碼
```
#include
#include
#include
#include
#include
#include
#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>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]>=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<=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