看懂但是不完全懂长链剖分
长链剖分#
总体上和重链剖分挺像的.
首先定义重儿子为 : 子树深度最深的儿子.
然后剩下的是轻儿子.
连向重儿子的是重边,重边连成重链.
每个点都在唯一长链中,长链必然不相交
实现就和重链剖分差不多.
len
记录最深能达到的深度.
void dfs(int u,int _f){
fe(i,u) {
int v = e[i].to;
if (v != _f){
dfs(v,u);
if(len[v] > len[son[u]])
son[u] = v;
}
}
len[u] = len[son[u]] + 1;
}
优化 DP#
在维护 与深度有关 的信息时,从重儿子快速继承信息,然后暴力统计轻儿子信息,和 dsu on tree 很像.
这个 与深度有关 不是特别明确,其实就是下标和深度有关那种感觉,而不是其他什么玩意.
因为每个点仅属于一条长链,且轻儿子全部为长链顶,所以时间复杂度线性.
CF161D Distance in Tree#
求一无根树内长度恰好为 的路径数量.
点分治是 的,能不能更好一些 ?
考虑长链剖分优化朴素的树形 DP.
令 表示在 的子树到 距离为 的结点个数.
因为 ,暴力转移然后乘法原理是可以通过本题的.
复杂度 .
优化后复杂度为 .
为了方便的继承信息,用指针 ptr[i]
指向点 继承的信息.
这样也使得统计信息只使用一个数组,指针只表示某个结点信息的首个位置罢了.
然后这玩意因为暴力合并轻儿子的部分常数大,和我的树状数组 + 点分治一样快,离谱.
此题最优解居然是 dsu on tree,常数小就为所欲为吗
Code :
int head[N],ecnt = -1;
struct Edge {
int nxt,to;
}e[N << 1];
inline void AddEdge(int st,int ed) {
e[++ecnt] = (Edge) {head[st],ed},head[st] = ecnt;
e[++ecnt] = (Edge) {head[ed],st},head[ed] = ecnt;
}
int k;
int ans;
int fa[N];
int len[N],hson[N];
int *ptr[N],buc[N];
int cnt;
void dfs1(int u,int _f) {
fa[u] = _f;
fe(i,u) {
int v = e[i].to;
if(v == _f) continue;
dfs1(v,u);
if(len[v] > len[hson[u]])
hson[u] = v;
}
len[u] = len[hson[u]] + 1;
}
inline void Add(int u,int v) {
ff(i,std::max(0,k - len[u]),std::min(len[v],k))
ans += ptr[v][i] * ptr[u][k - i - 1];
ff(i,0,len[v])
ptr[u][i + 1] += ptr[v][i];
}
void dfs2(int u,int top) {
if(u == top) {
ptr[u] = buc + cnt;
cnt += len[u];
}
else ptr[u] = ptr[fa[u]] + 1;
fe(i,u) {
int v = e[i].to;
if(v == fa[u] || v == hson[u])
continue;
dfs2(v,v);
}
if(hson[u]) dfs2(hson[u],top);
if(k < len[u]) ans += ptr[u][k];
++ptr[u][0];
fe(i,u) {
int v = e[i].to;
if(v == fa[u] || v == hson[u])
continue;
Add(u,v);
}
}
CF1009F Dominant Indices#
似乎这题还是 dsu on tree 比较方便.
[POI2014] HOT-Hotels#
此题和上面两题不太相同,不能用树上启发式合并.
对于没加强的,暴力 DP 即可.
但是加强版就不行了.
然后设计一下状态 :
: 子树内到 距离为 的点数.
: 子树内到 距离均为 的点对数.
然后乘法原理.
好像讲得太草率了
看代码吧
我是不是得了 wind_whisper 综合征啊
Code :
int head[N],ecnt = -1;
struct Edge {
int nxt,to;
}e[N << 1];
inline void AddEdge(int st,int ed) {
e[++ecnt] = (Edge) {head[st],ed},head[st] = ecnt;
e[++ecnt] = (Edge) {head[ed],st},head[ed] = ecnt;
}
int n;
int len[N],son[N];
ll tmp[N << 2];
ll *f[N],*g[N],*id = tmp;
ll ans;
void dfs(int u,int _f){
fe(i,u) {
int v = e[i].to;
if (v != _f){
dfs(v,u);
if(len[v] > len[son[u]])
son[u] = v;
}
}
len[u] = len[son[u]] + 1;
}
void dp(int u,int _f){
if(son[u]) {
f[son[u]] = f[u] + 1;
g[son[u]] = g[u] - 1;
dp(son[u],u);
}
f[u][0] = 1;
ans += g[u][0];
fe(i,u) {
int v = e[i].to;
if(v == _f || v == son[u]) continue;
f[v] = id;
id += len[v] << 1;
g[v] = id;
id += len[v] << 1;
dp(v,u);
ff(j,0,len[v] - 1){
if(j) ans += f[u][j - 1] * g[v][j];
ans += g[u][j + 1] * f[v][j];
}
ff(j,0,len[v] - 1){
g[u][j + 1] += f[u][j + 1] * f[v][j];
if(j) g[u][j - 1] += g[v][j];
f[u][j + 1] += f[v][j];
}
}
}
int mian() {
init_IO();
mems(head,-1);
n = read();
ff(i,1,n - 1) AddEdge(read(),read());
dfs(1,1);
f[1] = id;
id += len[1] << 1;
g[1] = id;
id += len[1] << 1;
dp(1,0);
write(ans);
end_IO();
return 0;
}
优化贪心#
BZOJ 3252 攻略#
题意概述 :
一棵 个点的有根树,点有点权,选出 条从根节点到叶结点的简单路径,使得这些路径的并的点权和最大.
求这个点权和.
然后你可以发现这个结构和长链剖分完的结构很想,长链也是不相交,于是不会多次统计贡献,长链也都到达叶子结点.
于是取权值大的就行,求 前 个,开个堆即可.
求 k 级祖先#
给定一棵有根树,给出多个询问,形同 表示求点 向上跳 个结点的祖先.
首先直接树上倍增就是 的了.
试将其优化为
首先还是树上倍增,但是询问的时候只跳 二进制最高的那一位,就是跳 步.
链头预处理暴力在链上上下跳到达的点.
然后跳到所在长链的链头,靠预处理的信息求解即可.
求 级祖先找不到啥实际应用上的题啊... 一搜都是些 dsu on tree 和 树形DP 就能做的题...
希望以后能用上吧.
Code :
模板题直接给出了每个点的父亲,于是不需要加反边,dfs 的时候也没有传值传入父亲结点.
正常写的时候别忘了加反边啊.
以及我这个模板常数好大啊...
int head[N],ecnt = -1;
struct Edge {
int nxt,to,w;
}e[N];
inline void AddEdge(int st,int ed) {
e[++ecnt] = (Edge) {head[st],ed};
head[st] = ecnt;
}
int dep[N];
int anc[N][20];
int hson[N],len[N],top[N];
std::vector<int> upp[N],low[N];
void dfs1(int u) {
len[u] = dep[u] = dep[anc[u][0]] + 1;
fe(i,u) {
int v = e[i].to;
anc[v][0] = u;
for(int j = 0;anc[v][j];++j)
anc[v][j + 1] = anc[anc[v][j]][j];
dfs1(v);
if(len[v] > len[u])
len[u] = len[v],hson[u] = v;
}
}
void dfs2(int u,int t) {
top[u] = t;
if(u == t) {
int p = u;
ff(i,0,len[u] - dep[u])
upp[u].push_back(p),p = anc[p][0];
p = u;
ff(i,0,len[u] - dep[u])
low[u].push_back(p),p = hson[p];
}
if(hson[u]) dfs2(hson[u],t);
fe(i,u) {
int v = e[i].to;
if(v == hson[u]) continue;
dfs2(v,v);
}
}
int l2[N];
inline int query(int u,int k) {
if(!k) return u;
u = anc[u][l2[k]];
k -= (1 << l2[k]),k -= dep[u] - dep[top[u]];
u = top[u];
return k >= 0 ? upp[u][k] : low[u][-k];
}
作者:AstatineAi
出处:https://www.cnblogs.com/AstatineAi/p/15852933.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现