1. 程式人生 > >kruskal重構樹學習筆記

kruskal重構樹學習筆記

## 內容 按照 $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