树形DP-换根DP-简记

【树形dp 换根法 BFS】2581. 统计可能的树根数目 Alice 有一棵 n 个节点的树,节点编号为 0 到 n - 1 。树用一个长度为 n - 1 的二维整数数组 edges 表示,其中 edges[i] = [ai, bi] ,表示树中节点 ai 和 bi 之间有一条边。 Alice 想要 Bob 找到这棵树的根。她允许 Bob 对这棵树进行若干次 猜测 。每一次猜测,Bob 做如下事情: 选择两个 不相等 的整数 u 和 v ,且树中必须存在边 [u, v] 。 Bob 猜测树中 u 是 v 的 父节点 。 Bob 的猜测用二维整数数组 guesses 阅读详情

导入

在普通树形DPDPDP中,一般就使用一个DPDPDP数组,代表以i为根的子树中的最值,但是对于某些题目中一个数组已经不够用,此时该加入什么,以及转移方程是什么,用一个题目来说明。

题目:P3478

给定一个 nnn 个点的树,请求出一个结点,使得以这个结点为根时,所有结点的深度之和最大。

按照普通 dpdpdp 来考虑,dpdpdp 数组状态为以i为根的子树中的深度之和,用 sumsumsum 数组记录i以i为根的子树中的节点数,如果现在在 xxx,yyy 是 xxx 的孩子,那么转移就是。

dpx=Σdpy+sumy dp_x = \Sigma {dp_y+sum_y} dpx​=Σdpy​+sumy​

即(y中)每个点都会往后再走一步走到x,所以要加sumysum_ysumy​。

可是这样就结束了吗?并没有。

因为当i变为整个树的根时,它的孩子还是它的孩子,但是它的父亲也变成孩子了。

所以还要用一个数组( dp2dp2dp2 ),储存除了以 iii 为根的子树的的深度之和。

那么转移就是它父亲向上的+它父亲向下的-它那支对父亲的影响+此点到父亲那些。

如图:

在这里插入图片描述

转移:

dp2x=dp2fa+dpfa−(dpx+sumx)+(sum[1]−sum[x]) dp2_x = dp2_{fa} + dp_{fa} - (dp_x+sum_x) + (sum[1]-sum[x]) dp2x​=dp2fa​+dpfa​−(dpx​+sumx​)+(sum[1]−sum[x])

然后这边给出代码:

//P2986来的
#include <bits/stdc++.h>
using namespace std;
#define int long long
int sum[1000005];//i的子树中一共有几头牛(包括i)
int down[1000005];//i的子树中那些牛来到这不方便值
int up[1000005];//除了i的子树中那些,其他点来这的不方便值
    int n;
vector <int> a[1000005];
int cnt[1000005];
void dfs(int x,int f){
    sum[x] = 1;
    for (int i = 0; i < a[x].size(); i++){
        int y = a[x][i];
        if (y != f){
            dfs(y,x);
            sum[x] += sum[y];
            down[x] += down[y]+sum[y];
        }
    }
    //cout << down[x]<<" " << x << " \n";
}

void dfs2(int x,int f){
    if (f!=0){
       up[x] = up[f]+down[f]-(down[x]+sum[x])+(sum[1]-sum[x]);//注意sum[1]而不是n...
        //+(sum[1]-sum[x])同上
        //up[f]+down[f]-(down[x]+sum[x]),除了我这条枝干到f外所有点到f的不方便程度
        //注意+sum[x]!
    }
    for (int i = 0; i < a[x].size(); i++){
        int y = a[x][i];
        if (y != f) dfs2(y,x);
    }
}
signed main(){
    ios::sync_with_stdio(false);  // 关闭 C 和 C++ 输入输出同步
    cin.tie(nullptr);         
    cout.tie(nullptr);   
    cin >> n;
    for (int i = 1; i <= n-1; i++){
        int u,v;
        cin >>u >> v;
        a[u].push_back(v);
        a[v].push_back(u);
    }
    dfs(1,0);
    dfs2(1,0);
    int max_ = -114514,id;
    for (int i = 1; i <= n; i++){
        if (max_ < down[i]+up[i]){
            max_ = down[i]+up[i];
            id = i;
        }
        //cout << up[i] << " " << down[i] << "\n";
    }
    cout << id;
    return 0;
}

题目:P9437

这里是引用

轻松得出转移,sumsumsum 代表节点数:

dpx=Σdpy∗10len(ax)+sumy∗ax dp_x = \Sigma dp_y*10^{len(a_x)}+sum_y*a_x dpx​=Σdpy​∗10len(ax​)+sumy​∗ax​

lenlenlen 函数为这个数是几位数。

这转移的解释:

加上 yyy 的那些延升到 xxx 的路(即 dpydp_ydpy​ 每一项往大移一下(∗10len(ax)*10^{len(a_x)}∗10len(ax​),提取公因数,从 dpydp_ydpy​ 每一项提出来),每一项再加入axa_xax​ (sumy∗axsum_y*a_xsumy​∗ax​))

那dp2dp2dp2怎么转移?

依旧参考这个图。

在这里插入图片描述
可以得出:

父亲向上的:dp2fadp2_{fa}dp2fa​

它父亲向下的:dpfadp_{fa}dpfa​

它那支对父亲的影响:(dpx∗10len(w[fa])+sumx∗afa)(dp_x*10^{len(w[fa])}+sum_{x}*a_{fa})(dpx​∗10len(w[fa])+sumx​∗afa​)

其实这里直接抄 dpdpdp 的转移就行。

定义 ans=dp2fa+dpfa−(dpx∗10len(a[fa])+sumx∗afa) ans = dp2_{fa}+dp_{fa}-(dp_x*10^{len(a[fa])}+sum_{x}*a_{fa})ans=dp2fa​+dpfa​−(dpx​∗10len(a[fa])+sumx​∗afa​)

那此点到父亲那些的贡献就是ans∗10len(a[x])+sum2x∗axans * 10^{len(a[x])}+sum2_x*a_xans∗10len(a[x])+sum2x​∗ax​

sum2sum2sum2 代表 ansansans 里有几项,这个贡献跟 dpdpdp 的转移差不多,不再过多赘述了。

sum2sum2sum2的更新:

依旧按照那个图来

sumx=sumfa+sum2fa−sumxsum_x = sum_{fa} + sum2_{fa}-sum_xsumx​=sumfa​+sum2fa​−sumx​

这边可以得出代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 998244353;//变量名终于不写boeing757了!
int dp[1000004];//以i为起点的(考虑i的树)
int dp2[1000004];//以i为结尾的(考虑非i的树)
int sum[1000004];//有几条路线(考虑i的树)
int sum2[1000004];//有几条路线(考虑非i的树)
int w[1000004];
int ten[1000004];
vector <int> a[1000004];
int len(int x){
    if(x == 0) return 1;
    int cnt = 0;
    while(x) cnt++, x /= 10;
    //peepsick说不用flag。。。
    return cnt;
}
void dfs(int x,int fa){
    //自己到自己
    dp[x] = w[x];
    sum[x] ++;//自己到自己是一条
    for (int i = 0; i < a[x].size(); i++){
        int y = a[x][i];
        if (y != fa){
            dfs(y,x);
            sum[x] += sum[y];
            dp[x] += dp[y]%mod*ten[len(w[x])]%mod+sum[y]%mod*w[x]%mod;//加上y的那些延升到x的路(即dp[y]每一项往大移一下(*ten[len(w[x])],提取公因数,从dp[y]每一项提出来),每一项再加入w[x](sum[y]*w[x]))
        }
    }
}
void dfs2(int x,int fa){
    if (fa != 0){
        sum2[x] = sum[fa] + sum2[fa]-sum[x];
        int self = dp[x]%mod*ten[len(w[fa])]%mod+sum[x]%mod*w[fa]%mod;//减去自己那支(直接抄dp的转移)
        int ans = (dp2[fa]%mod+dp[fa]%mod-self+mod) %mod;
        dp2[x] = ((ans%mod * ten[len(w[x])]%mod)%mod+sum2[x]%mod*w[x]%mod)%mod;//,注意加上w[x]并*位数
    }
    for (int i = 0; i < a[x].size(); i++){
        int y = a[x][i];
        if (y != fa) dfs2(y,x);
    }
}
signed main(){
    ten[0] = 1;
    for (int i = 1; i<= 10; i++) ten[i] = ten[i-1] * 10%mod;
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> w[i];
    for (int i = 1; i <= n-1; i++){
        int u;
        cin >> u;
        a[u].push_back(i+1);
        a[i+1].push_back(u);
    }
    dfs(1,0);
    dfs2(1,0);
    int ans = 0;
    for (int i = 1; i <= n; i++){
        (ans += dp[i]%mod+dp2[i]%mod)%=mod;
    }
    cout << ans;
    return 0;
}

最后给出一些换根的题目

在这里插入图片描述

第一题为模板。

前 333 道题较为简单。

后面考察一些思考,以及代码实现有细节(至少对我来说)。

完结!

Starlink vs Kuiper vs OneWeb:三大低轨卫星网络架构对比与实战应用场景解析 本文对比分析了Starlink、Kuiper和OneWeb三大低轨卫星网络架构的技术差异与商业应用场景。从轨道设计、通信性能到商业落地,详细解析了各系统在近地卫星网络领域的独特优势与挑战,为未来全球宽带服务格局提供专业见解。 阅读详情

相关推荐

鸿蒙实战开发教程-注册登录页面

在密码输入框中,只要设置TextInput的类型就可以实现带眼睛logo的密码输入框效果:

2401_82546228的博客 3561

换根dp

换根dp解决的问题示例讲解例题 解决的问题 基于树上dp的基本模型,题目初始不给出根节点,需要遍历每一个根节点,达到某一个根节点的最佳情况。 暴力的思路是循环每个节点作为根节点,同时在遍历整个树,时间复杂度是O(n2)O(n^2)O(n2)。 换根dp主要就是将时间复杂度降到O(n)O(n)O(n),在根结点切换时,直接通过一些已经计算过的数据在O(1)O(1)O(1)就能得到另一个根的结果。 一般是通过二次扫描,第一次dfs获得预处理数据,第二次dfs进行根节点切换 示例讲解 需要找到以x点为根结点的树,

weixin_44232130的博客 2195

中国IP发展之面面观

谢学军/邱善勤2006-1-25 9:29:28电子经理世界       IP远没有我们当初想象的那么简单   当SoC设计方法学刚刚传到国内的时候,学界为之欢呼雀跃,由于IP/SoC设计技术的使用,可以将发达国家用几年甚至十几年积累起来的设计技术直接为我所用,使中国的集成电路跳过漫长的积累阶段,迅速站到较高的起点上。因此在当时看来,IP/SoC设计技术是我国提升设计水平、发展

klxdmm305的专栏 1188

【学习笔记】详解换根法(换根DP)

【学习笔记】详解换根法(换根DP)

weq2011的博客 6248

解决TextMeshPro在AB打包中的字体冗余问题:源码修改全记录

本文详细介绍了如何解决TextMeshPro在AB打包中的字体冗余问题,通过源码改造和工程实践优化Unity项目的性能。内容包括资源加载路径重构、AB打包策略优化及运行时加载方案,有效减少包体体积和构建时间,提升内存效率。

weixin_42558257的博客 185

Python自动化测试报告:飞书群机器人消息卡片实战

本文详细介绍了如何利用Python和飞书群机器人实现自动化测试报告的智能通知。通过构建可定制的消息卡片,将测试结果(如通过数、失败数)以清晰、美观的格式自动推送至团队群聊,并支持一键跳转查看详细报告,从而显著提升团队协作与问题响应效率。

k6l7m8n9的博客 811

树形dp——换根dp

通过观察,我们发现,右边的深度减少了,左边的增加了,减少的节点为未换根之前以0为根节点经过2的所有路径上的节点,增加的为没有经过2的。我们假设某个节点为根,将无根树化为有根树,在搜索dfs统计子树的深度和,则可以用一次搜索算出以该节点为根时的深度和,其时间复杂度为。就表示为上升的深度,不经过child的子节点树都增加,不经过的节点数目就是从节点数目减去经过的节点数目。如图,我们的根节点就是0,接下来我们选取子节点2作为新的根节点,对其进行换根。但这样求解出的答案只是以该节点为根的,并不是最优解。

m0_74176921的博客 862

【数电课程设计】---出租车计价器

文章目录1、任务要求2、方案3、收获 1、任务要求 2、方案 3、收获

LZY 1万+

动态规划:(2):树形dp与换根dp

本文介绍了树形动态规划(DP)的基本概念和应用场景。首先通过一道热身题展示了树结构问题的解题思路,随后详细讲解了树形DP的实现方法,包括状态转移方程设计和递归求解过程。文章还介绍了换根DP技术,用于求解以所有节点为根的最优解问题,并通过例题演示了如何利用子树信息优化计算。最后,作者总结了树形DP的核心思想,并强调通过具体例题理解抽象算法的学习方法。全文以通俗易懂的方式呈现了树形DP的基本框架和典型应用。

jzoj_ylz的博客 1295

ros2 gazebo仿真阿克曼小车搭载livox mid360雷达流程

流程简介:仿真环境的搭建需要安装ROS2和Gazebo。使用的是Humble版本,需要确认是否已经正确安装。然后是阿克曼小车模型的URDF,需要包括底盘、转向机构、驱动轮等,需要参考现有的阿克曼模型,比如ClearPath的Husky,但需要调整参数。Livox Mid360雷达在Gazebo中的仿真可能没有现成的插件,需要使用通用3D LiDAR插件来模拟。IMU的仿真相对简单,但需要注意噪声模型,以接近真实数据。

weixin_43802726的博客 3212

AMS TMF8820/21/28 SPAD 掩码和模式选择

为了实现最快的测距周期,将报告周期设置在测距周期以下的毫秒,以确保不会有等待时间。除了8X8固定了pixel的位置意外,其他模式客户可以设计自己的spad掩码,并将spad单独分配给通道。对于这些spad掩码,有一些约束。左下角提示红色代表光学中心,绿色方框表示单个spad,每个MP里可能有2X3个spad,也有2X5个spad,也有3X6+3个spad,这部分适配光学,最大的程度的利用spad区域。以下是几种预定义的spad掩码,非常灵活的配置,来适配各种3X3,4X4,8X8的模式选择。

dream it possible 701

换 根 dp

1.首先 dfs1 得到每个节点子树的节点个数,然后得到dp[1](以1节点为根的答案),dp[子]=dp[父] - size[子] + n - size[子] 得到其相邻子节点的答案。2.假设以u为根节点的收益是dp[u],如果以v为根节点,对于以u为根,r染色v时收益增加n-size[v];2.设置sum数组:固定某个节点为根节点(比如下图以1为根节点),各节点到其子树所有节点的距离和,得到sum[1],就是dp[1]用下题引入换根dp:简而言之换根dp就是通过父节点的答案得到相邻子节点的答案。

2301_80275822的博客 405

博途S7-1500T 使用工艺对象驱动液压轴(含SimaHydTO库)

利用工艺对象控制液压轴位置含PLC控制和仿真程序文档资料下载地址如下:液压比例伺服阀的相关应用知识请参看下面的博客文章:液压位置闭环控制(比例伺服阀应用)_RXXW_Dor的博客-优快云博客。

RXXW_Dor的博客 2901

集训DAY8之树形 DP与换根DP

树形动态规划(Tree DP)是动态规划在树结构上的具体应用,它充分利用了树的递归特性和层次结构来设计高效的求解策略。作为无环连通图,树的特殊性质为动态规划提供了天然的计算框架。在树形 DP 中,我们通常将问题分解为子树问题:对每个节点计算状态值,然后根据父子关系将这些状态值向上传递。这种自底向上(后序遍历)或自顶向下(前序遍历)的计算方式,与树的遍历顺序完美契合。状态定义通常与子树信息密切相关状态转移依赖于子节点的状态无环特性避免了循环依赖问题计算顺序由树的遍历顺序决定。

Liyankai1523900的博客 845

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

树形DP(树形背包+换根DP)

通过题目可以知道,一条路径上的流量取决于最小流量边,所以这是一个维护最小值的问题。对于度数只为1的还需要特别注意,度数唯一不一定是叶子结点,也可能是根节点,当换根时,根节点变成叶子结点,我们先来想如果根节点固定的时候如何做,比较容易的DP,其实也就是递推,没有任何决策问题。我来思考,如果是在树中间换根怎么办,我们发现,父亲节点还需要同时加入父亲父亲的节点情况。节点就是根节点,不再是父亲节点的时候,此时的值刚好就是。家常便饭了,写了好几遍,没啥好说的,正常独立集问题。题面:见ACwing287。

zxsure的博客 933

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

树形dp换根

换根解决的是“不定根”的树形dp问题。该类题目的特点是:给定一个树形结构,需要以每个节点为根进行一系列统计。 方法为两次扫描来求解: 第一次扫描时,任选一个点为根,在“有根树”上执行一次树形dp,在回溯时,自底向上的状态转移。 第二次扫描时,从第一次选的根出发,对整根树执行一个dfs,在每次递归前进行自顶向下的转移,计算出换根后的解。 题目: 有一个树形的水系,由 N-1 条河道和 N 个交叉点组...

weixin_44316314的博客 2230

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

动态规划-路径相关树形DP and 换根DP

在换根DP中,我们首先任选一棵树中的一个节点作为新的根节点,然后根据原树的边和节点关系,重新组织形成以该节点为根的树。在计算过程中,我们可以利用子节点的计算结果来更新父节点的值,直到最终计算出整棵树的结果。通过换根DP技巧,我们可以在同一棵树上不同的根节点下进行动态规划计算,并获取到所有可能的结果。然后,对于每个节点,我们可以根据其与原根节点的关系,将以新根节点为根的子树的结果转化为以原根节点为根的子树的结果。在路径相关的树形动态规划中,我们需要考虑从根节点到叶子节点的路径,并根据问题的要求计算相关的值。

estiny_123123的博客 2070

树形dp<1>——换根dp

换根dp

seanli1008的博客 1006

【动态规划 & 树形DP】树的换根DP(换根DP模板)

树形 DP 中的换根 DP 问题又被称为二次扫描,通常不会指定根结点,并且根结点的变化会对一些值,例如子结点深度和、点权和等产生影响。所谓换根DP,就是基于原有的状态,通过相邻节点进行转换后,现有的状态仅仅只需要进行微小的变动即可达到完美相邻状态间的切换。通常需要两次 DFS,第一次 DFS 预处理诸如深度,点权和之类的信息,在第二次 DFS 开始运行换根动态规划。那么考虑完了两个根节点之间状态的转变,根相邻节点的状态应该如何变化?的相邻节点,这些点的树高状态并不会改变,读者可以自己思考。

Cauchy的博客 1527

树形|换根 DP总结

树形 DP,即在树上进行的 DP。由于树固有的递归性质,树形 DP 一般都是递归进行的。

skyflying266的博客 692

换根 dp

那么就会出现重复计算的情况。注意:换根时要注意第一次。的部分,此时如果直接将。

m0_72761451的博客 740

换根 DP 简介

● 换根 DP 通常遵循一个固定的两遍 DFS 流程

hnjzsyjyj的专栏 1170

树形换根DP

树形DP之换根DP

S_et_HKD的博客 602

『树形DP·换根法』Accumulation Degree

题目描述 有一个树形的水系,由 N-1 条河道和 N 个交叉点组成。 我们可以把交叉点看作树中的节点,编号为 1~N,河道则看作树中的无向边。 每条河道都有一个容量,连接 x 与 y 的河道的容量记为 c(x,y)。 河道中单位时间流过的水量不能超过河道的容量。 有一个节点是整个水系的发源地,可以源源不断地流出水,我们称之为源点。 除了源点之外,树中所有度数为 1 的节点都是入海口,可以吸收无限多...

Ronaldo7_ZYB的博客 1768

树上dp之换根dp

换根dp是树上dp的一种我们在什么时候需要用到换根dp呢?当题目询问的属性,是需要当前结点为根时的属性,这个时候,我们就要使用换根dp换根dp的基本思路:假设题目询问的的属性为x通常我们会进行两次dfs第一次dfs,我们选取任意一个结点作为给出的无根树的根,对其进行dfs,并求出这个根的x,以及一些其他辅助数组(即节点与其子树的一些属性关系)第二次dfs,我们记dp[i]为对于结点i而言,节点i作为树的根时,我们要求的属性x。

Se_ren_di_pity的博客 1392

树形DP/换根DP 习题

树形DP,树形背包,换根DP的习题讲解

xishanmeigao2918的博客 534
上一篇: 微积分-简记
B757-300
博客等级 码龄4年 259粉丝 52原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值