【学习笔记】长链剖分

简述

用于优化于深度状态有关的 dp 转移,时空复杂度均为 O(n) 。其中每个节点的状态是用指针分配内存。

CF1009F Dominant Indices

题意:设 d(u,x)u 子树中到 u 距离为 x 的节点数。对于每个点,求一个最小的 k,使得 d(u,k) 最大。

#include<bits/stdc++.h> using namespace std; const int mx=1e6+5; vector<int> g[mx]; //内存池 int buf[mx]; //指针动态分配空间 int *f[mx],*now=buf,dep[mx],son[mx],ans[mx]; int n; void dfs1(int x,int fa) { for(auto y:g[x]) { 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 fa) { f[x][0]=1; if(son[x]) { f[son[x]]=f[x]+1; dfs2(son[x],x); ans[x]=ans[son[x]]+1; } for(auto y:g[x]) { if(y==fa||y==son[x]) continue; f[y]=now; now+=dep[y]; dfs2(y,x); for(int j=1;j<=dep[y];j++) { f[x][j]+=f[y][j-1]; if(f[x][j]>f[x][ans[x]]||f[x][j]==f[x][ans[x]]&&j<ans[x]) ans[x]=j; } } //细节问题 if(f[x][ans[x]]==1) ans[x]=0; } signed main() { scanf("%d",&n); for(int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); g[x].push_back(y),g[y].push_back(x); } dfs1(1,0); f[1]=now; now+=dep[1]; dfs2(1,0); for(int i=1;i<=n;i++) printf("%d\n",ans[i]); }

[POI2014]HOT-Hotels

好难啊。。。

定义 f[u][j] 为以 u 为根的子树,距离为 j 的节点个数。g[u][j] 定义为下图 (x,y) 点对个数:

请添加图片描述
枚举子节点 v 转移:

  1. g[u][j]*f[v][j-1]->ans
  2. g[v][j]*f[u][j+1]->ans
  3. f[u][j]->f[v][j+1]

比较棘手的是 g[u][j] 的转移,分类讨论:

  1. (x,y) 在同一子树内,g[v][j]->g[u][j-1]
    请添加图片描述
  2. (x,y) 不在同一子树内,用 f[] 转移:f[v][j-1]*f[u][j]->g[u][j]请添加图片描述
    注意 g[]f[] 先转移。朴素转移是 O(n) ,时空复杂度 O(n^2)

可以用长链剖分优化。具体地,先继承重儿子的状态,考虑 ans+=g[u][0] 。再暴力转移轻儿子。注意 g 指针要前移。

时空复杂度均为 O(n)

总结:本题第一眼是换根 dp ,但是无法优化。本题巧妙利用状态设置+长链剖分,将距离作为状态,成功优化到线性。

#include<bits/stdc++.h> #define int long long using namespace std; const int mx=1e6+5; vector<int> G[mx]; int buf[mx<<2]; int *f[mx],*g[mx],*now=buf,dep[mx],son[mx]; int n; int res; //长链剖分优化 dp //uodate : 2021-07-23 void dfs1(int u,int fa) { for(auto v:G[u]) { if(v==fa) continue; dfs1(v,u); if(dep[v]>dep[son[u]]) son[u]=v; } dep[u]=dep[son[u]]+1; } void dfs2(int u,int fa) { f[u][0]=1; if(son[u]) { f[son[u]]=f[u]+1; g[son[u]]=g[u]-1; //g[v][j]->g[u][j-1] dfs2(son[u],u); } res+=g[u][0]; for(auto v:G[u]) { if(v==fa||v==son[u]) continue; f[v]=now; now+=dep[v]<<1; g[v]=now; now+=dep[v]<<1; dfs2(v,u); for(int j=1;j<=dep[v];j++) { res+=g[u][j]*f[v][j-1]+g[v][j]*f[u][j-1]; } for(int j=0;j<dep[v];j++) { g[u][j]+=g[v][j+1]; } for(int j=1;j<=dep[v];j++) { g[u][j]+=f[v][j-1]*f[u][j]; } for(int j=1;j<=dep[v];j++) { f[u][j]+=f[v][j-1]; } } } signed main() { scanf("%lld",&n); for(int i=1;i<n;i++) { int x,y; scanf("%lld%lld",&x,&y); G[x].push_back(y),G[y].push_back(x); } dfs1(1,0); f[1]=now; now+=dep[1]<<1; g[1]=now; now+=dep[1]<<1; dfs2(1,0); printf("%lld",res); }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530318.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(13)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示