【过题记录】 7.28 (树上dp,背包,换根,基环树)

[ZJOI2007] 时态同步


分析:

不难发现,中断点就是叶子节点,
首先,所有叶子节点的高度肯定就等于最深的那个叶子节点的深度。
且不可能去调整最深的叶子结点的深度了。
这样经过一遍dfs之后我们可以计算出每个叶子需要增加的高度。
然后我们发现,如果能调整高度,肯定是要调整尽可能高的那条边,因为这条边调整之后,他所属的叶子节点的深度全部都会加1
于是我们发现,一条边所能调整的最大值,一定是他全部儿子节点所需调整的最小值。
在通过一遍从下往上dfs可以求出每条边调整的最小值
然后再跑一遍从上往下dfs累计答案即可。


Code

#include<bits/stdc++.h>
using namespace std;#define int long long const int N = 5e5+100;
typedef pair < int , int > pii;
#define fi first
#define se second
#define pb push_back
int n;
int rt;
vector < pii > a[N];
int f[N];
int s[N],maxs;
int mins[N];void Dfs(int x,int faa){for (int i = 0; i < a[x].size(); i++){int y = a[x][i].fi,v = a[x][i].se;if (y == faa) continue;s[y] = s[x]+v;Dfs(y,x);}maxs = max(maxs,s[x]);
}
void Dp1(int x,int faa){bool f = 1;mins[x] = 1e18;for (int i = 0; i < a[x].size(); i++){int y = a[x][i].fi , v = a[x][i].se;if (y == faa) continue;f = 0;Dp1(y,x);mins[x] = min(mins[x],mins[y]);}if (f) mins[x] = maxs-s[x];
}int ans = 0;
void Dp2(int x,int faa,int sum){for (int i = 0; i < a[x].size(); i++){int y = a[x][i].fi; if (y == faa) continue;ans+=mins[y]-sum;Dp2(y,x,mins[y]);}
}signed main(){cin.tie(0);ios::sync_with_stdio(false);cin>>n>>rt;for (int i = 1,x,y,z; i < n; i++)cin>>x>>y>>z,a[x].pb({y,z}),a[y].pb({x,z});Dfs(rt,0);Dp1(rt,0);Dp2(rt,0,0);cout<<ans<<endl;
}

有线电视网


分析:

比较明显是一个树上背包问题。
人就相当于是物品,钱就相当于是价值。
f [ i ] [ j ] f[i][j] f[i][j]表示以i为根的树,选j个观众所能获得的最大金币值
可以如下转移(类似分组背包):
f [ x ] [ j ] = m a x ( f [ x ] [ j ] , f [ x ] [ j − i ] + f [ y ] [ i ] − e d . v ) f[x][j] = max(f[x][j],f[x][j-i]+f[y][i]-ed.v) f[x][j]=max(f[x][j],f[x][ji]+f[y][i]ed.v)
答案就是 f [ 1 ] [ i ] > = 0 f[1][i]>=0 f[1][i]>=0的最大i值


Code

#include<bits/stdc++.h>
using namespace std;const int N = 3e3+100,inf = 3e5+100;
int n,m;
typedef pair < int , int > pii;
#define pb push_back
#define fi first
#define se secondvector < pii > a[N];
int f[N][N];
int v[N];int Dfs(int x,int faa){bool ff = 1;int sum = 0;f[x][0] = 0;for (int i = 0; i < a[x].size(); i++){int y = a[x][i].fi , v = a[x][i].se;if (y == faa) continue; ff = 0;int t = Dfs(y,x); sum+=t;for (int vv = sum; vv >= 1; vv--)for (int j = 1; j <= t; j++)if (vv >= j) f[x][vv] = max(f[x][vv],f[x][vv-j]+f[y][j]-v);}if (ff){f[x][0] = 0;f[x][1] = v[x]; return 1;}return sum;
}int main(){cin.tie(0);ios::sync_with_stdio(false);cin>>n>>m;for (int i = 1; i <= n; i++)for (int j = 1; j <= n; j++) f[i][j] = -inf;for (int i = 1; i <= n-m; i++){int k; cin>>k;for (int j = 1,y,z; j <= k; j++)cin>>y>>z,a[i].pb({y,z}),a[y].pb({i,z});}for (int i = n-m+1; i <= n; i++) cin>>v[i];Dfs(1,0);int Max = 0;for (int i = n; i >= 0; i--)if(f[1][i]>=0) {cout<<i<<endl;return 0;}return 0; 
}

Nearby Cows G


分析:

题目要我们求离每一个点距离不超过k的点的点权和
其实是一个比较明显的换根dp的题目
这是一颗无根树,但是我们先假设树的根节点为1号点
f [ i ] [ j ] f[i][j] f[i][j]表示以i为根节点,他的子树里距离当前节点不超过j的点权和
于是就有下面很显然的转移方程:
f [ x ] [ j ] = f [ y ] [ j − 1 ] + v [ x ] f[x][j]=f[y][j-1]+v[x] f[x][j]=f[y][j1]+v[x]
这样子跑完一遍dfs之后可以得到以1为根的答案
但是显然不是每个点都得到了答案
所以我们需要跑一遍换根dp,求出每一个点的答案
其实换根dp的关键,就是看当前节点根父亲节点之间的关系,如何通过父亲节点的答案转移到当前子节点的答案
我们这个时候如果 令f的定义中的子树去掉
那么 f [ x ] [ k ] f[x][k] f[x][k]显然就是每个点的答案。
那么我们如何转移,才能将上面的定义转化为这个定义呢?
关键就是找出非子树内的距离当前点不超过k的点的权值和是多少
我们发现这个可以通过父亲来转移
f [ y ] [ k ] + = f [ x ] [ k − 1 ] f[y][k]+=f[x][k-1] f[y][k]+=f[x][k1]
但是我们发现这个时候会有一点重复,因为 f [ x ] [ k − 1 ] f[x][k-1] f[x][k1]有一部分答案是包含当前子树的,而当前子树的答案我们已经有了
所以还需要减去 f [ y ] [ k − 2 ] f[y][k-2] f[y][k2]
综上, f [ y ] [ k ] + = f [ x ] [ k − 1 ] − f [ y ] [ k − 2 ] f[y][k]+=f[x][k-1]-f[y][k-2] f[y][k]+=f[x][k1]f[y][k2]


#include<bits/stdc++.h>
using namespace std;#define int long longconst int N = 1e5+100;
int f[N][30];
int n,k;
vector < int > a[N];
int ans[N];
#define pb push_back
#define fi first
#define se second
int v[N];void Insert(int x,int y){a[x].pb(y);
}void Dfs(int x,int faa){for (int i = 0; i < a[x].size(); i++){int y = a[x][i]; if (y == faa) continue;Dfs(y,x);for (int kk = 1; kk <= k; kk++)f[x][kk]+=f[y][kk-1];}for (int i = 0; i <= k; i++) f[x][i]+=v[x];
}void Dp(int x,int faa){ans[x] = f[x][k];for (int i = 0; i < a[x].size(); i++){int y = a[x][i]; if (y == faa) continue;for (int kk = k; kk >= 1; kk--){f[y][kk]+=f[x][kk-1];if (kk >= 2) f[y][kk]-=f[y][kk-2];}Dp(y,x);}
}signed main(){cin.tie(0);ios::sync_with_stdio(0);cin>>n>>k;for (int i = 1,x,y; i < n; i++)cin>>x>>y,Insert(x,y),Insert(y,x);for (int i = 1; i <= n; i++) cin>>v[i];Dfs(1,0);Dp(1,0);for (int i = 1; i <= n; i++) cout<<ans[i]<<endl;return 0;
}

发现环


分析:

基环树找环
用拓扑排序去找环

Code

#include<bits/stdc++.h>
using namespace std;const int N = 1e5+100;
int du[N];
vector < int > a[N];
#define pb push_back
int n;
bool vi[N];int main(){cin>>n;for (int i = 1,x,y; i <= n; i++)cin>>x>>y,a[x].pb(y),a[y].pb(x),du[x]++,du[y]++;queue < int > q;for (int i = 1; i <= n; i++)if (du[i] == 1) q.push(i);while (q.size()){int x = q.front(); q.pop();vi[x] = 1;for (int i = 0; i < a[x].size(); i++){int y = a[x][i];du[y]--;if (du[y] == 1) q.push(y);}}for (int i = 1; i <= n; i++)if (!vi[i]) cout<<i<<' ';return 0;
}

城市环路


分析:

这道题其实本来应该是一个基环树dp的题
但是这边没采用这个方法
其实我们发现,隔一个人选一个点,有点类似于奇偶性
只能选取奇偶性相同的点。
那么对于环上的点,其实我们只需要找出任意两个相连的点,将两个点分别作为树的根跑一遍树形dp即可
f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1]表示选/不选当前点的最优答案
那么最终答案就是 m a x ( f [ x ] [ 0 ] , f [ y ] [ 0 ] ) max(f[x][0],f[y][0]) max(f[x][0],f[y][0])
限定两个点分别不选,是因为其实这两个点在环上是连着的,这样一定满足条件。
找出在环上的任意两个点可以采用并查集
最后一个加边使形成一个环,此时两个点一定处在同一集合


#include<bits/stdc++.h>
using namespace std;#define int long longconst int N = 1e6+100;
int fa[N];
vector < int > a[N];
#define pb push_back
int n;
int v[N];
int f[N][3];
double k;int getfa(int x){return fa[x] == x?x:fa[x] = getfa(fa[x]);
}void Dp(int x,int faa){bool ff = 1;for (int i = 0; i < a[x].size(); i++){int y = a[x][i]; if (y == faa) continue;ff = 0;Dp(y,x);f[x][1]+=f[y][0];f[x][0]+=max(f[y][1],f[y][0]);}f[x][1]+=v[x];
}signed main(){cin.tie(0);ios::sync_with_stdio(false);cin>>n;for (int i = 1; i <= n; i++) fa[i] = i;int st,ed;for (int i = 1; i <= n; i++){int x,y; cin>>v[i]>>y;int X = getfa(i) , Y = getfa(y);if (X == Y){st = i; ed = y; continue;}a[i].pb(y); a[y].pb(i);fa[X] = Y;}Dp(st,0);int ans = f[st][0];memset(f,0,sizeof f);Dp(ed,0);ans = max(ans,f[ed][0]);cout<<ans;return 0;
}

骑士


分析:

这道题跟上一题基本一样
但是需要注意的是这道题可能是基环树森林
对于森林里的每个数都累加一遍答案即可

Code

#include<bits/stdc++.h>
using namespace std;#define int long longconst int N = 1e6+100;
int fa[N];
vector < int > a[N];
#define pb push_back
int n;
int v[N];
int f[N][3];
bool vi[N];
int st[N],ed[N];
int cnt = 0;int getfa(int x){return fa[x] == x?x:fa[x] = getfa(fa[x]);
}vector < int > live;
void Dp(int x,int faa){live.pb(x);bool ff = 1;for (int i = 0; i < a[x].size(); i++){int y = a[x][i]; if (y == faa) continue;ff = 0;Dp(y,x);f[x][1]+=f[y][0];f[x][0]+=max(f[y][1],f[y][0]);}f[x][1]+=v[x];
}signed main(){cin.tie(0);ios::sync_with_stdio(false);cin>>n;for (int i = 1; i <= n; i++) fa[i] = i;
//	int st,ed;for (int i = 1; i <= n; i++){int x,y; cin>>v[i]>>y;int X = getfa(i) , Y = getfa(y);if (X == Y){st[++cnt] = i; ed[cnt] = y; continue;
//			st = i; ed = y; continue;}a[i].pb(y); a[y].pb(i);fa[X] = Y;}int ans = 0;for (int i = 1; i <= cnt; i++){Dp(st[i],0);int now = f[st[i]][0];for (int j = 0; j < live.size(); j++) f[live[j]][0] = f[live[j]][1] = 0;live.clear();Dp(ed[i],0); now = max(now,f[ed[i]][0]);for (int j = 0; j < live.size(); j++) f[live[j]][0] = f[live[j]][1] = 0;live.clear();ans+=now;}cout<<ans;return 0;
}

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

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

相关文章

古文:文天祥《正气歌》

原文 正气歌 【作者】文天祥 【朝代】宋 余囚北庭&#xff0c;坐一土室。室广八尺&#xff0c;深可四寻。单扉低小&#xff0c;白间短窄&#xff0c;污下而幽暗。当此夏日&#xff0c;诸气萃然&#xff1a;雨潦四集&#xff0c;浮动床几&#xff0c;时则为水气&#xff1b;涂泥…

内容营销专家刘鑫炜:极狐车自燃风波自救,堪称品牌危机公关范本

近日&#xff0c;极狐电车自燃事件在社交媒体上迅速发酵&#xff0c;尤其是厂家在事故现场的第一反应——先抠车标、覆盖黑布的行为&#xff0c;更是引发了公众的广泛质疑与愤慨。这一突发事件不仅考验着极狐汽车的产品安全性能&#xff0c;更对其品牌危机公关能力提出了严峻挑…

YAML 语法规范

文章目录 YAML 语法规范一、简介二、基本语法三、高级语法四、示例解析五、注意事项YAML 语法规范 一、简介 YAML(YAML Ain’t Markup Language)是一种专门用来写配置文件的语言,具有简洁、易读、易解析等特点。YAML的设计理念是为人类和机器之间的沟通提供一种更加直观、…

Chiplet SPI User Guide 详细解读

目录 一. 基本介绍 1.1.整体结构 1.2. 结构细节与功能描述 二. 输入输出接口 2.1. IO Ports for SPI Leader 2.2. IO Ports for SPI Follower 2.3. SPI Mode Configuration 2.4. Leader IP和Follower IP功能图 三. SPI Programming 3.1. Leader Register Descripti…

基于FPGA的数字信号处理(19)--行波进位加法器

1、10进制加法是如何实现的&#xff1f; 10进制加法是大家在小学就学过的内容&#xff0c;不过在这里我还是帮大家回忆一下。考虑2个2位数的10进制加法&#xff0c;例如&#xff1a;15 28 43&#xff0c;它的运算过程如下&#xff1a; 个位两数相加&#xff0c;结果为5 8 1…

【elementui】记录如何重命名elementui组件名称

在main.js中&#xff0c;就是引入elementui的文件中 import ElementUI from element-ui import { Tree } from element-uiVue.use(ElementUI) Vue.component(el-tree-rename, Tree)

c++——vector容器详解

vector vector概述主要特点和优势&#xff1a;使用示例&#xff1a;成员类型 vector函数构造函数迭代器函数size和capacity函数操作函数 vector-bool特点和用法&#xff1a;示例用法&#xff1a; vector概述 C 中的 std::vector 是一个动态数组&#xff0c;是标准模板库&#…

苹果 iCloud 钥匙串是什么?如何查看及对其进行设置?

在当今的数字世界中安全性和便利性是人们关注的两大重点。无论是社交媒体账户、还是网购平台等&#xff0c;几乎每个在线服务都需要登录账户。如何安全地管理和存储这些账户密码成为了用户们的一大挑战。 iCloud 钥匙串 我们先来看一看什么是 iCloud 钥匙串&#xff0c;iClou…

Photoshop钢笔工具

一、钢笔工具概述 Photoshop中的钢笔工具&#xff08;快捷键为P&#xff09;是矢量绘图工具&#xff0c;主要用于精确绘制直线、曲线以及开放或闭合的路径。钢笔工具通过锚点和方向线来定义路径的形状&#xff0c;可以绘制出平滑的曲线和直线段&#xff0c;非常适合用于抠图、…

Redis:事务

1. 简介 可以一次性执行多个命令&#xff0c;本质是一组命令的集合。一个事务中的所有命令都会序列化&#xff0c;按顺序的串化执行&#xff0c;不允许被其他其他命令插入&#xff0c;不许加塞 即将要执行的命令放入队列中&#xff0c;此时该队列的所有命令就是一个事务&#x…

浏览器同源策略详解、主流的跨域解决方案、深入理解跨域请求概念及其根因

1. 什么是同源策略 跨域问题其实就是浏览器的同源策略造成的。 同源策略限制了从同一个源加载的文档或脚本如何与另一个源的资源进行交互。这是浏览器的一个用于隔离潜在恶意文件的重要的安全机制。同源指的是&#xff1a;协议、端口号、域名必须一致。 下表给出了与 URL http…

如何让你的C语言程序打印的log多一点色彩?(超级实用)

接着上一篇文章《由字节对齐引发的一场“血案“ 》 在平常的调试中&#xff0c;printf字体格式与颜色都是默认一致的。 如果可以根据log信息的重要程度&#xff0c;配以不同的颜色与格式&#xff0c;可以很方便的查找到要点。 1、printf字体显示语法说明 printf(“\033[显示…

Hive环境搭建(内置数据库)

实验目的】 1) 了解hive的作用 2) 熟练hive的配置过程&#xff08;内置数据库&#xff09; 【实验原理】 Hive的架构是由Client、Metastore、Driver、Compiler构成&#xff0c;执行流程是编译器可以将一个Hive QL转换成操作符&#xff0c;操作符是Hive中的最小处理单元。…

如何查看操作系统的性能指标:CPU、内存、磁盘、网络

目录 本系列专栏 CPU篇 CPU使用率&#xff1a;top CPU负载&#xff1a;uptime CPU核心使用情况&#xff1a;mpstat -P ALL 1 上下文切换&#xff1a;vmstat 1 CPU等待 IO时长&#xff1a;iostat -x 1 CPU的频率&#xff1a;lscpu 或者 cat /proc/cpuinfo | grep "cpu MHZ…

oracle读写时相关字符集详解

服务器端操作系统&#xff08;Oracle linux&#xff09;字符集 服务器端数据库字符集 客户端操作系统&#xff08;Oracle linux&#xff09;字符集 客户端工具sqlplus字符集 结论1&#xff1a;客户端工具sqlplus的会话&#xff0c;使用的字符集&#xff0c;是数据库字符集。…

Spring三级缓存是如何作用的

什么是三级缓存 singletonObjects&#xff1a; 一级缓存&#xff0c;用于保存实例化、注入、初始化完成的bean实例【完全体】earlySingletonObjects&#xff1a; 二级缓存&#xff0c;用于保存实例化完成的bean实例singletonFactories&#xff1a; 三级缓存&#xff0c;用于保…

【Vue】Vue-Router(路由)

一、基本概念 路由&#xff08;Route&#xff09;&#xff1a; 路由表示一个映射关系&#xff0c;即 URL 路径到组件的映射。在 Vue-Router 中&#xff0c;一个路由对象通常包含两个主要部分&#xff1a;path&#xff08;路径&#xff09;和 component&#xff08;组件&#x…

java面向对象总结

java面向对象篇到这里就已经结束了&#xff0c;有什么不懂的地方可以逐一进行重新观看。希望大家能够从入门到起飞。 Java面向对象基础篇综合训练&#xff08;附带全套源代码及逐语句分析&#xff09;-&#xff1e;基于javabeen Java面向对象进阶篇综合训练&#xff08;附带全…

2024 Java 高分面试宝典 一站式搞定技术面

前言 每年9月和10月&#xff0c;被业界称为“金九银十”&#xff0c;这是人才市场一年中最活跃的时期。此时&#xff0c;企业为了来年的业务扩展&#xff0c;纷纷加大招聘力度&#xff0c;空缺岗位众多&#xff0c;招聘需求集中。同时&#xff0c;初秋的招聘活动也避开酷暑&am…

操作系统:高级IO

高级IO 1.关于IO IO的基本类型&#xff1a; I代表输入(Input): 从外部设备或来源&#xff08;如键盘、鼠标、文件、网络&#xff09;读取数据到计算机中。示例&#xff1a;用户键入的文本、从文件读取的数据、从网络接收到的数据包。 O代表输出(Output): 将计算机处理后的数据发…