长链剖分
与重链剖分类似,但是重儿子是根据深度判定,可以处理深度相关问题。具体的,继承重儿子的信息,暴力合并其他轻儿子的链。由于所有链长为 \(n\),且每条链只会在链头被合并,所以时间复杂度为线性。
有性质:
-
自己祖先的链长一定大于自己当前链长。由此可以推出 \(x\) 的 \(k\) 级祖先的链长大于等于 \(len_x+k\)。
-
从任意一个节点出发,到达根节点至多根号条不同的链。由于每次跳到不同的链则新链链长大于原先的链,于是依次最少使用 \(1,2,3,\cdots\),故最多根号条链。
COGS 3184 收益
长链剖分模板,选最大的 \(k\) 条链即可。
CF1009F
长链剖分模板,继承重儿子加上轻儿子的数组即可做到线性。
COGS 2652 秘术「天文密葬法」
首先可以看出这题是分数规划,二分 \(mid\) 则转为判定性问题判定是否存在长为 \(m\) 的路径,使得 \(\sum a_i - mid \times \sum b_i \le 0\),转为长链剖分。但是在继承重儿子的时候需要整个数组加值,可以维护当前数组总共加过 \(p\),在加入新点 \(a_x\) 的时候,将 \(a_x\) 设置为 \(-p\),然后将 \(p\) 加上需要加的值,查询的时候真实值就为 \(a+p\)。
P5904 [POI 2014] HOT-Hotels 加强版
维护 \(f_{x,i}\) 为 \(x\) 子树内,距离 \(x\) 为 \(i\) 的点个数,\(g_{x,i}\) 表示 \(x\) 子树内,点对 \((u,v)\) 满足 \(dis(u,lca)=dis(v,lca) \land dis(x,lca)+j=dis(u,lca)\) 的个数。
则有转移:
使用指针继承维护,贴个指针维护代码。
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INT_MAX (int)(1e18)
const int N=1e5+10;
int n;
int head[N],nxt[N<<1],ver[N<<1];
inline int read(){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
return x*f;
}
int len[N],son[N];
void dfs(int u,int v){
for(int i=head[u];i;i=nxt[i]){
int to=ver[i];
if(to==v) continue;
dfs(to,u);
if(len[to]>len[son[u]]) son[u]=to;
}
len[u]=len[son[u]]+1;
}
int ans;
int tmp[N<<2],*f[N],*g[N],*id=tmp;
void dfs1(int u,int v){
f[u][0]=1;
if(son[u]) f[son[u]]=f[u]+1,g[son[u]]=g[u]-1,dfs1(son[u],u);
ans+=g[u][0];
for(int i=head[u];i;i=nxt[i]){
int dao=ver[i];
if(dao==v||dao==son[u]) continue;
f[dao]=id,id+=len[dao]<<1,g[dao]=id,id+=len[dao]<<1,dfs1(dao,u);
for(int j=1;j<=len[dao];j++)
ans+=f[dao][j-1]*g[u][j];
for(int j=0;j<len[dao]-1;j++)
ans+=f[u][j]*g[dao][j+1];
for(int j=0;j<len[dao];j++)
g[u][j+1]+=f[dao][j]*f[u][j+1],f[u][j+1]+=f[dao][j];
for(int j=1;j<len[dao];j++) g[u][j-1]+=g[dao][j];
}
}
signed main(){
n=read();
for(int i=1;i<n;i++){
int u=read(),v=read();
nxt[i]=head[u],head[u]=i,ver[i]=v;
nxt[i+n]=head[v],head[v]=i+n,ver[i+n]=u;
}
dfs(1,0);
f[1]=id,id+=len[1]<<1,g[1]=id,id+=len[1]<<1;
dfs1(1,0);
cout<<ans<<"\n";
return 0;
}
P5903 【模板】树上 K 级祖先
根据性质,我们可以对于每条链链顶,记录其沿链长向下链长个点和其向上链长个点的祖先信息。然后倍增预处理每个点的 \(2^x\) 级祖先。则查询 \(x\) 的 \(y\) 级祖先时,可以先将 \(x\) 跳 \(2^{\log y}\) 步,则此时链长大于 \(2^{\log y}\),同时也大于 \(y- 2^{\log y}\)。接着将 \(x\) 跳到链顶,检查此时 \(y\) 剩余正负,对应向上向下跳即可做到 \(O(n \log n) - O(1)\)。

浙公网安备 33010602011771号