最近學習flutter感想,爭取一星期能用flutter寫專案
阿新 • • 發佈:2021-11-10
倍增求LCA
用 \(f_{x,k}\) 表示 \(x\) 向根節點走了 \(2^k\) 步到達的節點。
易得:
所以遍歷一次圖可預處理出。
對於一組 \((x,y)\) 的詢問:
- 選擇深度更深的節點,倍增跳到與另一點深度相同處。
- 若此時兩點相同,則得出答案。
- 否則 \(x,y\) 在不重合的前提下一隻向上倍增。
- 最後 \(x\) 或 \(y\) 的父親一定是 LCA。
inline void bfs(){ t=(int)log(n)/log(2)+1; q.push(root);d[1]=1; while(q.size()){ int x=q.front();q.pop(); for(int i=head[x];i;i=nxt[i]){ int y=ver[i]; if(d[y])continue; d[y]=d[x]+1,f[y][x]=0,q.push(y); for(int j=1;j<=t;j++)f[y][j]=f[f[y][j-1]][j-1]; q.push(y); } } } inline int Lca(int x,int y){ if(dep[x]<dep[y])swap(x,y); for(int i=t;i>=0;i--) if(dep[f[x][i]]>=dep[y])x=fa[x][i]; if(x==y)return x; for(int i=t;i>=0;i--) if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; return f[x][0]; }
P4180 [BJWC2010]嚴格次小生成樹
容易得到,嚴格次小生成樹一定是最小生成樹替換一條邊得到的。
先求出最小生成樹,考慮加入未在最小生成樹內的邊 \((x,y,z)\),那麼樹上 \(x\) 到 \(y\) 的路徑上一定有一條邊被替換。
令 \(x\) 到 \(y\) 的路徑上最小邊權為 \(z_1\),嚴格次小邊權為 \(z_2\), 那麼:
- 當 \(z\ne z_1\) 時,替換 \((x,y,z)\) 得到的嚴格次小生成樹一定換去 \(z_1\) 這條邊。
- 否則換去 \(z_2\) 這條邊。
用樹上倍增求出 \(z_1\),\(z_2\),此過程和求 LCA 相似。
建議畫圖理解。
引理1:三個點的兩兩 LCA 必有一對重合。
反證法易證。
引理2:答案一定是另一個 LCA。
如果選另一個LCA,兩個 LCA 中間的邊被經過1次,否則被經過2次。
由於一種狀態想要縮小隻有一種方案,所以可以把縮小的方案建成一棵樹,答案就是起始和結束狀態的 LCA。
考慮不暴力縮小,而是一次把一邊縮小到比另一邊小。
最後二分距離 LCA 的距離後倍增即可。