Living-Dream 系列笔记 第71期

news/2024/9/18 19:13:09/文章来源:https://www.cnblogs.com/XOF-0-0/p/18337360

众所周知,换根 dp 是非常套路的。换根真好玩(

换根 dp:

当不同节点作为根时,dp 结果不一致,若枚举每个节点作为根,则时间复杂度过高,在此种情形下,可使用 换根 dp 处理相邻两节点间的贡献,从而达到快速换根的效果。

使用场景:

对于一棵树,寻找以某节点 \(u\) 为根时取得的 最大值 / 最小值 / 方案数

实现步骤:

  1. 任选一节点作为根,跑一遍树形 dp,得到 \(dp_i\) 表示以 \(i\) 根的子树的 最大值 / 最小值 / 方案数。
  2. \(f_i\) 表示以 \(i\)全局根 时的 最大值 / 最小值 / 方案数,初始 \(f_1=dp_1\)
  3. 从根再次 dfs,自父节点向子节点转移 \(f_i\)

P3478

\(dp_i\) 表示以 \(i\) 为根的子树的节点全局深度之和。

(令 \(dp_i\) 表示为全局 / 局部信息依题而定,哪个方便做选哪个)

初始:\(dp_{cur}=dep_{cur}\)

转移:\(dp_{cur}=dp_{cur}+dp_i\)\(cur\)\(i\) 的父节点)。

\(f_i\) 表示以 \(i\)全局根的节点深度之和。

初始:\(f_1=dp_1\)

答案:\(\max\{f_i\}\)

转移:

image

如图,以 \(nxt\) 为根的子树往上升,其子树内所有点的深度会减 \(1\);而以 \(cur\) 为根的子树往下降,其子树内所有点的深度会加 \(1\)

于是有转移:

\[f_{nxt}=f_{cur}-siz_{nxt}+(n-siz_{nxt}) \]

\[=f_{cur}+n-2 \times siz_{nxt} \]

\(siz_{nxt}\) 表示以 \(nxt\) 为根的子树大小)。

code
#include<bits/stdc++.h>
#define int long long
using namespace std;const int N=1e6+5;
int n;
vector<int> G[N<<1];
int dp[N],f[N],siz[N],dep[N];void dfs1(int cur,int fa){siz[cur]=1;dp[cur]=dep[cur];for(int i:G[cur]){if(i==fa) continue;dfs1(i,cur);dep[i]=dep[cur]+1;siz[cur]+=siz[i];dp[cur]+=dp[i];}
}
void dfs2(int cur,int fa){for(int i:G[cur]){if(i==fa) continue;f[i]=f[cur]+n-2*siz[i];dfs2(i,cur);}
}signed main(){cin>>n;for(int i=1,u,v;i<n;i++)cin>>u>>v,G[u].push_back(v),G[v].push_back(u);dep[1]=1;dfs1(1,0);f[1]=dp[1];dfs2(1,0);int ans=0,p=0;for(int i=1;i<=n;i++)if(ans<f[i])ans=f[i],p=i;cout<<p; return 0;
}

P2986

这题实质即为上题加个边权。

\(dp_i\) 表示以 \(i\) 为根的子树的节点到它的带权路径和(局部)。

初始:\(dp_{cur}=0\)

转移:\(dp_{cur}=dp_cur+dp_i+siz_i \times w\)\(cur\)\(i\) 的父节点,\(w\) 表示边 \(cur \to i\) 的边权)。

\(f_i\) 表示以 \(i\) 为全局根的带权路径和的最小值。

初始:\(f_1=dp_1\)

答案:\(\min\{f_i\}\)

转移:

image

如图,以 \(nxt\) 为根的子树往上升,子树内贡献不变,且子树内的所有节点都无需经过 \(cur \to nxt\) 这条边;以 \(cur\) 为根的子树往下降,子树内的所有节点都必须经过 \(cur \to nxt\) 这条边。

于是有转移:

\[f_{nxt}=(dp_{nxt}-siz{nxt} \times w)+((f_{cur}-dp_{nxt}) + (tot-siz_{nxt}) \times w) \]

\[=dp_{nxt}+(tot-2 \times siz_{nxt}) \times w \]

\(w\) 表示 \(cur \to nxt\) 这条边的边权,\(siz_{nxt}\) 表示 \(nxt\) 子树内的牛的数量

code
#include<bits/stdc++.h>
#define int long long
using namespace std;const int N=1e6+5;
int n,tot,c[N];
struct E{ int v,w; };
vector<E> G[N<<1];
int dp[N],f[N],siz[N];void dfs1(int cur,int fa){siz[cur]=c[cur];dp[cur]=0;for(auto i:G[cur]){if(i.v==fa) continue;dfs1(i.v,cur);siz[cur]+=siz[i.v];dp[cur]+=dp[i.v]+siz[i.v]*i.w;}
}
void dfs2(int cur,int fa){for(auto i:G[cur]){if(i.v==fa) continue;f[i.v]=f[cur]+(tot-2*siz[i.v])*i.w;dfs2(i.v,cur);}
}signed main(){cin>>n;for(int i=1;i<=n;i++) cin>>c[i],tot+=c[i];for(int i=1,u,v,w;i<n;i++)cin>>u>>v>>w,G[u].push_back({v,w}),G[v].push_back({u,w});dfs1(1,0);f[1]=dp[1];dfs2(1,0);int ans=1e18;for(int i=1;i<=n;i++)ans=min(ans,f[i]);cout<<ans; return 0;
}

CF1187E

诈骗题。

我们先按照常规套路进行分析。

容易发现在第一次选点后的选点操作都是固定的。考虑换根 dp。

\(dp_i\) 表示以 \(i\) 为根的子树的全局最大权值(当然局部也可)。

初始:\(dp_{cur}=siz_{cur}\)\(siz_i\) 表示以 \(i\) 为根的子树大小)。

转移:\(dp_{cur}=dp_{cur}+dp_i\)

\(f_i\) 表示以 \(i\) 为全局根的最大权值。

初始:\(f_1=dp_1\)

转移:

image

如图,以 \(nxt\) 为根的子树往上升,子树内贡献不变,且子树内所有节点均无需对以 \(cur\) 为根的子树产生贡献;以 \(cur\) 为根的子树往下降,子树内的所有节点都必须对以 \(nxt\) 为根的子树产生贡献。

于是有转移:

\[f_{nxt}=dp_{nxt}+(f_{cur}-dp_{nxt}-siz_{nxt})+(n-siz_{cur}) \]

\[=f_{cur}+n-2 \times siz_{cur} \]

然后我们发现这就是 P3478 的转移方程。

这是因为每次进行染色,贡献都是染色节点的子树大小。

而每次染色后下一个被染色的一定是它的子节点,

这就导致每个节点在它的每一个祖先染色时都贡献了 \(1\)

加起来就是它的深度,

因此所有点的贡献之和就是深度之和。

code
#include<bits/stdc++.h>
#define int long long
using namespace std;const int N=1e6+5;
int n;
vector<int> G[N<<1];
int dp[N],f[N],siz[N];void dfs1(int cur,int fa){siz[cur]=1;for(int i:G[cur]){if(i==fa) continue;dfs1(i,cur);siz[cur]+=siz[i];dp[cur]+=dp[i];}dp[cur]+=siz[cur];
}
void dfs2(int cur,int fa){for(int i:G[cur]){if(i==fa) continue;f[i]=f[cur]+n-2*siz[i];dfs2(i,cur);}
}signed main(){cin>>n;for(int i=1,u,v;i<n;i++)cin>>u>>v,G[u].push_back(v),G[v].push_back(u);//dep[1]=1;dfs1(1,0);f[1]=dp[1];dfs2(1,0);int ans=0;for(int i=1;i<=n;i++)if(ans<f[i])ans=f[i];cout<<ans; return 0;
}

CF1324F

看到 \(0,1\) 求贡献,首先考虑将 \(0\) 转化为 \(-1\)

于是这题通过转化后,求差变为了求和。

接着我们发现,选包含某个点的连通子图,则必须包含其子树。

又因为要求出每个点的最大值,因此这题就变成了 换根版 的 最大子树和。

\(dp_i\) 按照那题求出即可。

\(f_i\) 表示以 \(i\) 为全局根的最大值。

初始:\(f_1=dp_1\)

答案:所有的 \(f_i\)

转移:

子树是必选的,子树外的如果贡献 \(>0\) 则可选,否则不选。

另外,子树外的贡献由子树内的贡献决定,如果子树内贡献 \(>0\),则子树外的要去掉子树内的贡献,否则可以全选。

于是有转移:

\[f_{nxt}=dp_{nxt}+\max(\max(f_{cur}-\max(dp_{nxt},0),0)) \]

code
#include<bits/stdc++.h>
using namespace std;const int N=2e5+5;
int n,a[N];
int dp[N],f[N];
vector<int> G[N<<1];void dfs1(int cur,int fa){dp[cur]=a[cur];for(int i:G[cur]){if(i==fa) continue;dfs1(i,cur);dp[cur]=max(dp[cur],dp[cur]+dp[i]);}
}
void dfs2(int cur,int fa){for(int i:G[cur]){if(i==fa) continue;f[i]=dp[i]+max(f[cur]-max(dp[i],0),0);dfs2(i,cur);}
}int main(){cin>>n;for(int i=1,x;i<=n;i++)cin>>x,a[i]=(x?x:-1);for(int i=1,u,v;i<n;i++)cin>>u>>v,G[u].push_back(v),G[v].push_back(u);dfs1(1,0);f[1]=dp[1];dfs2(1,0);for(int i=1;i<=n;i++)cout<<f[i]<<' ';return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/776302.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

公司运营数据分析大屏,非专业者也能轻松上手

在这个数据洪流的时代,企业的每一步发展都深深刻画在数字的轨迹之中。如何精准捕捉这些瞬息万变的信息,将其转化为推动企业前行的智慧力量?答案,或许就藏在一面高效、直观的公司运营数据分析大屏之中。想象一下,当晨光初照,公司的大厅中央,一块巨大的LED屏幕缓缓亮起,它…

vmware 更新时间报错修复

Loading mirror speeds from cached hostfile Could not retrieve mirrorlist http://mirrorlist.centos.org/?release=7&arch=x86_64&repo=os&infra=stock error was 14: curl#6 - "Coulsolve host: mirrorlist.centos.org; 未知的错误" 问题原因 出现…

旧笔记本安装Win8.1实录

一个憨批的安装Win8.1纪录昨天发现一台尘封已久的Lenovo ideapad Y550,给它装上了Windows 10 然后第二天系统挂掉了 挂的原因是半夜万恶之源Windows更新开始造孽了 刚好没电 文件全坏了真 解除封印 因为文件已经没了 我索性直接重装系统,降级到Win8.1 真香! 系统是Win8.1 wit…

Phpcms的数据库配置文件是哪个?怎样修改数据库配置信息?

Phpcms的数据库配置文件位置在:caches\configs\database.php 如果只是数据库账号密码等信息变更了,那么找到文件后修改对应的数据库链接信息就可以了! 如果搬家后域名也变更了,那么除了以上两个文件,就需要多修改一下两个配置文件: caches/configs/system.php phpsso_ser…

建立数据库连接时出错”或者“Error establishing a database connection”错误

错误记录: 访问网站时提示“建立数据库连接时出错”或者“Error establishing a database connection”错误。 错误原因: 该问题通常是由于网站程序不能正常连接MySQL数据库导致,需要排查服务器上MySQL数据库服务。 解决方案: 1,首先检查网站数据库状态是否正常 2,核对配…

易优CMS模板标签relevarticle相关文档

[基础用法] 标签:relevarticle描述:通过前3个TAG标签或前3个关键词,检索整站文档标题中含有tag标签或者关键词的相关文档,进行关联。在没有tag标签情况下,就以前3个关键词检索文档标题进行关联。这个标签随着数据量的增加可能会比较影响检索性能。 提示:使用该标签之前,…

Kotlin 运算符详解:算术、赋值、比较与逻辑运算符全解析

## Kotlin 运算符 - **用途**: 对变量和值执行操作。 - **示例**:```kotlinvar x = 100 + 50 // 150``` - **分类**:- **算术**: `+`, `-`, `*`, `/`, `%`, `++`, `--`.- **赋值**: `=`, `+=`, `-=`.- **比较**: `==`, `!=`, `<`, `>`, `<=`, `>=`.- **逻辑**: `&a…

Jmeter(五十二)PostMan的json格式文件转换为jmx文件

他年我若为青帝,报与桃花一处开---黄巢 一、环境准备 1. maven2. postman3. jmeter二、将PostMan的接口请求导出来 export即可 三、拉取项目并构建git clone https://github.com/Loadium/postman2jmx.git拉取完成进行构建cd postman2jmx mvn package进入构建完成的target/pos…

杭电多校 2024 游记

前言 和 ppip 还有 b6e0_ 组的队,team102。 每场都是按 \(\bmod\ 3\) 开的题,\(0,1,2\) 分别对应的是 b6e0,我,ppip。 2024-07-19 Round 1 自己过了 2,8,12,5。2024-07-22 Round 2 自己过了 8,3,2。2024-07-26 Round 3 自己过了 8,11,12,5。2024-07-29 Round 4 自己过了 5,…

[学习笔记] 斜率优化

引入 斜率优化用于求解类似于 \(f_i = f_j + a_ib_j + c_i\) 使 \(f_i\) 最大或最小之类的形式的 DP 转移,标志就是其中有一项(如 \(a_ib_j\))与 \(i,j\) 均有关联。 求解 令 \(j\) 为 \(i\) 的最优决策点,也就是 \(f_i = f_j + a_ib_j + c_i\),我们将其进行一些移项可以得…

Educational Codeforces Round 168 (Rated for Div. 2) E.Level Up

E.Level UP二分答案 + 树状数组单调性:因为 \(k\) 越小,怪兽越是容易逃跑对每个怪物,二分答案出与之战斗的最小 \(k\) 值(阙值) 树状数组用于记录在当前位置之前,在不同的 \(k\) 下有多少个怪物可以战斗从前往后遍历,每个位置的阙值为下标,这样pt.get(x)就能得到在参数…

解决飞书 Linux 在屏幕分享时候的回音问题

问题 在 Linux 桌面环境中使用飞书时,有一个十分诡异的现象: 触发条件:使用飞书会议; 自己进行屏幕分享; 自己没有 mute,即自己没有关闭麦克风。现象:其他人讲话时会听到他自己的回音; 我自己听到的声音则是正常的。我的使用环境:飞书版本:7.18.11 Debian 12 + KDE +…