印度痛批亞馬遜和沃爾瑪:美國電商巨頭傲慢,甚至無視法律
阿新 • • 發佈:2021-06-28
長鏈剖分
定義:
長鏈剖分主要用於解決兩點之間鏈的問題。
實現:
思路與重鏈剖分相同,但是選擇重兒子時選擇子樹深度最大的兒子。
程式碼:
void dfs1(int x,int fa){ d[x]=d[fa]+1; f[x]=fa; for(int i=head[x];i;i=nxt[i]){ int y=ver[i]; if(y==fa) continue; dfs1(y,x); if(dep[y]>dep[son[x]]) son[x]=y; } dep[x]=dep[son[x]]+1;//子樹中的最大深度 } void dfs2(int x,int topfather){ top[x]=topfather; if(son[x]) dfs2(son[x],topfather); for(int i=head[x];i;i=nxt[i]){ int y=ver[i]; if(y==f[x]||y==son[x]) continue; dfs2(y,y); } }
當然,題目不同時,\(dfs2\) 會有所不同,具體情況具體分析。
性質:
- 所有鏈的長度和是 \(O(n)\) 級別的。
- 任意一個點的 \(k\) 次祖先 \(y\) 所在的長鏈的長度大於等於 \(k\)。
- 任意一個點向上跳重鏈次數不超過 \(\sqrt{n}\) 次。
作用:
優化dp:快速合併以深度為下標的子樹資訊
例題:P5904 [POI2014]HOT-Hotels 加強版
我們可以根據題意設定一個 \(dp\) 轉移,時間複雜度為 \(O(n^2)\)
所以,設 \(f[i][j]\) 表示以 \(i\) 為根的子樹中,距離當前點為 \(j\) 的點數。
\(g[i][j]\) 表示以i為根的子樹中,兩個點到 \(LCA\)
考慮轉移:
ans+=g[i][0];
ans+=g[i][j]*f[son[i]][j-1];
f[i][j]+=f[son[i]][j-1];
g[i][j]+=g[son[i]][j+1];
我們需要運用指標來將陣列的空間減少,因此在轉移中可以這樣:
int *f[N];
.....
f[i]=f[son[i]]-1;
g[i]=g[son[i]]-1;
整棵樹是鏈,時間複雜度將為 \(O(n)\) 。我們推廣到樹:
進行長鏈剖分,直接從重兒子轉移 \(O(1)\) ,從輕兒子轉移 \(O(\sum len)\)
因此總複雜度就是 \(O(n)\)
其餘狀態轉移暴力即可。
程式碼:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=4e5+5;
int n;
int d[N],dep[N],son[N];
ll *f[N],*g[N],p[N<<2],*o=p,ans;
int nxt[N],ver[N],tot,head[N];
void add(int x,int y){
ver[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs1(int x,int fa){
d[x]=d[fa]+1;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(y==fa) continue;
dfs1(y,x);
if(dep[y]>dep[son[x]]) son[x]=y;
}
dep[x]=dep[son[x]]+1;
}
/*
設f[i][j]表示x在i的子樹中,且d(x,i)=j的x的個數
g[i][j]為滿足x,y在i的子樹中且d(lca(x,y),x)=d(lca(x,y),y)=d(lca(x,y),i)+j
的數對(x,y)的個數
遞推公式:
ans=g[i][0]=sum f[x][j-1]*g[y][j+1](x,y屬於son[i])
g[i][j]=sum g[x][j+1](x屬於son[i])
f[i][j]=sum f[x][j-1](x屬於son[i])
*/
void dfs2(int x,int fa){
if(son[x]){
f[son[x]]=f[x]+1;
g[son[x]]=g[x]-1;
dfs2(son[x],x);
}
f[x][0]=1,ans+=g[x][0];
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(y==fa||y==son[x]) continue;
f[y]=o,o+=dep[y]<<1;
g[y]=o,o+=dep[y]<<1;
dfs2(y,x);
for(int i=0;i<dep[y];i++){
if(i) ans+=f[x][i-1]*g[y][i];
ans+=g[x][i+1]*f[y][i];
}
for(int i=0;i<dep[y];i++){
g[x][i+1]+=f[x][i+1]*f[y][i];
if(i) g[x][i-1]+=g[y][i];
f[x][i+1]+=f[y][i];
}
}
}
int main()
{
cin>>n;
for(int i=1,x,y;i<n;i++){
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
dfs1(1,0);
f[1]=o;o+=dep[1]<<1;
g[1]=o,o+=dep[1]<<1;
dfs2(1,0);
cout<<ans<<endl;
system("pause");
return 0;
}