前言

感觉是非常优秀的题目,写一篇题解记录一下。

HDU-7458 旅行(on Vjudge)

题面

题目描述

有一棵 \(n\) 个结点的无根树,每个结点都有对应的类型 \(c_i\) 和权重 \(w_i\) ,你需要在这棵树上规划若干次旅行。

对于一次旅行,你将从一个树上的一个结点出发,沿着树上的边进行旅行,最终到达另一个和起点类型相同的结点。

你会进行很多次旅行,但你希望对于每个结点,在所有旅行路线中最多只会经过一次。

一次旅行的价值是起始点和终止点的权重和,你需要规划旅行的方案使得旅行的总权重和最大。

输入格式

第一行输入一个正整数 \(t\ (1\le t\le 3)\) 代表数据组数。

对于每组数据:

第一行输入一个正整数 \(n\ (1\le n \le 2\times 10^5)\),代表树的点数。

第二行输入 \(n\) 个正整数 \(c_i\ (1\le c_i \le n)\) ,代表每个结点的类型。

第三行输入 \(n\) 个正整数 \(w_i\ (1\le w_i \le n)\) ,代表每个结点的权重。

接下来 \(n-1\) 行每行两个正整数 \(u_i,v_i\ (1\le u_i,v_i\le n,u_i\neq v_i)\) ,代表树上一条边,输入保证是一棵树。

输出格式

输出一行一个正整数代表答案。

输入输出样例

输入 #1

1
7
3 1 1 2 2 2 3
2 4 1 5 4 6 2
1 2
1 3
2 4
2 5
3 6
3 7

输出 #1

13

Hint

一种最优的旅行方案为:

旅行路线1:\(4\to 2\to 5\) ,价值为 \(9\)。

旅行路线2:\(1\to 3\to 7\),价值为 \(4\)。

价值总和为 \(13\)。

题解

我把本题的限制看成了恰好每个点经过一遍,于是浪费了一个晚上,在此警示后人。

注意到如果根节点走过或没走过状态确定,可以划分到每棵子树,考虑是树形 DP。设状态 \(f_{x,0}\) 表示子树 \(x\) 内 \(x\) 未走过的最大权值, \(f_{x,1}\) 表示子树 \(x\) 内 \(x\) 已走过的最大权值。\(f_{x,0}\) 的转移时容易的,因为不可能出现跨子树合并的贡献。

\[f_{x,0}=\sum_{v\in \operatorname{son}(x)}\max\{f_{x,0},f_{x,1}\}
\]

接下来我们考虑 \(f_{x,1}\),显然我们需要在子树 \(x\) 内寻找一个与 \(x\) 类型相同的点,然后合并。或者找到两个不同子树类型相同的点,合并顺便经过 \(x\)。考虑选择 \(y\) 作为合并的节点之一的贡献。设 \(x\to y\) 路径上的点为 \(a_1=x,a_2,\dots a_{k-1},a_k=y\),则可以算出贡献为下面式子。

\[w_x+w_y+f_{y,0}+\sum_{i=1}^{k-1}f_{a_i,0}-\max\{f_{a_{i+1},0},f_{a_{i+1},1}\}
\]

这个式子没有对齐,并不方便计算,于是我们稍微修改一下求和顺序,把下标对齐。

\[w_x+f_{x,0}+w_y+\sum_{i=2}^{k}f_{a_i,0}-\max\{f_{a_{i},0},f_{a_{i},1}\}
\]

选择两颗颜色相同的子树合并,就是 \(x\) 到这两个点的路径的贡献减去 \(f_{x,0}\),因为每一棵子树都恰好被按照 \(f_{x,0}\) 的方式多算了一次,直接减掉即可。代码中化简了这个式子。

注意到 \(w_x+f_{x,0}\) 是确定的,对于某种颜色的转移其实只需要使 \(w_y+\sum_{i=2}^{k}f_{a_i,0}-\max\{f_{a_{i},0},f_{a_{i},1}\}\) 最大。我们不妨把某棵子树某种颜色的最大值的集合存进一个 map。注意到这个式子只与 \(x\to y\) 的路径有关,在搜索回溯的时候加入当前节点,并对这个子树已有状态的集合使用懒标记增加逐步自底向上求出。我们还要支持合并两个集合,因为我们要合并多个子树的信息。

注意到这个合并可以使用启发式合并,对于每种颜色,选择最大的两个合并或选择根合并,顺便更新最大值的集合,就可以求出 \(f_{i,1}\)。时间复杂度 \(O(n\log^2 n)\)。

代码

顺便积累一个写法,C++17 以后,可以利用下列方式访问 map 中所有元素。(\([c,p]\) 中 \(c\) 为下标,\(p\) 为元素值)

for(auto [c,p]:g[b[v]])
#include <bits/stdc++.h>
using namespace std;
struct edge
{
long long v,nxt;
}e[600000];
long long t,n,u,v,h[300000],b[300000],c[300000],w[300000],f[300000][2],mx[300000],tg[300000],cnt=0;
map<long long,long long>g[300000];
inline long long read()
{
long long x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();}
return x*f;
} void add_edge(long long u,long long v)
{
e[++cnt].nxt=h[u];
e[cnt].v=v;
h[u]=cnt;
} void dsu(long long x,long long fa)
{
f[x][0]=f[x][1]=mx[x]=0;
for(int i=h[x];i;i=e[i].nxt)
if(e[i].v!=fa)dsu(e[i].v,x),f[x][0]+=mx[e[i].v];
for(int i=h[x];i;i=e[i].nxt)
if(e[i].v!=fa)
{
long long v=e[i].v;
if(g[b[v]].count(c[x]))f[x][1]=max(f[x][1],g[b[v]][c[x]]+tg[b[v]]+f[x][0]+w[x]);
if(g[b[v]].size()>g[b[x]].size())swap(b[v],b[x]);
for(auto [c,p]:g[b[v]])
if(g[b[x]].count(c))f[x][1]=max(f[x][1],p+tg[b[v]]+g[b[x]][c]+tg[b[x]]+f[x][0]);
for(auto [c,p]:g[b[v]])
if(g[b[x]].count(c))g[b[x]][c]=max(g[b[x]][c],p+tg[b[v]]-tg[b[x]]);
else g[b[x]][c]=p+tg[b[v]]-tg[b[x]];
}
if(g[b[x]].count(c[x]))g[b[x]][c[x]]=max(g[b[x]][c[x]],w[x]-tg[b[x]]);
else g[b[x]][c[x]]=w[x]-tg[b[x]];
mx[x]=max(f[x][0],f[x][1]),tg[b[x]]+=f[x][0]-mx[x];
} int main()
{
t=read();
while(t--)
{
n=read();
for(int i=1;i<=n;i++)h[i]=tg[i]=cnt=0,b[i]=i,g[i].clear();
for(int i=1;i<=n;i++)c[i]=read();
for(int i=1;i<=n;i++)w[i]=read();
for(int i=1;i<=n-1;i++)u=read(),v=read(),add_edge(u,v),add_edge(v,u);
dsu(1,0);
printf("%lld\n",mx[1]);
}
return 0;
}

HDU7458 旅行 题解的更多相关文章

  1. noip2012开车旅行 题解

    题目大意: 给出n个排成一行的城市,每个城市有一个不同的海拔.定义两个城市间的距离等于他们的高度差的绝对值,且绝对值相等的时候海拔低的距离近.有两个人轮流开车,从左往右走.A每次都选最近的,B每次都选 ...

  2. 洛谷 P5022 旅行——题解

    发现大部分题解都是O(n^2)的复杂度,这里分享一个O(n)复杂度的方法. 题目传送 首先前60%的情况,图是一棵无根树,只要从1开始DFS,每次贪心走点的编号最小的点就行了.(为什么?因为当走到一个 ...

  3. [HAOI2006]旅行 题解(kruskal)

    [HAOI2006]旅行 Description Z小镇是一个景色宜人的地方,吸引来自各地的观光客来此旅游观光.Z小镇附近共有N个景点(编号为1,2,3,-,N),这些景点被M条道路连接着,所有道路都 ...

  4. NOIP 2018旅行题解

    从佳木斯回来刷一刷去年没A的题 题目描述 小 Y 是一个爱好旅行的 OIer.她来到 X 国,打算将各个城市都玩一遍. 小Y了解到, X国的 nn 个城市之间有 mm 条双向道路.每条双向道路连接两个 ...

  5. luoguP1081 开车旅行 题解(NOIP2012)

    这道题是真滴火!(一晚上加一节信息课!) 先链接一下题目:luoguP1081 开车旅行 首先,这个预处理就极其变态,要与处理出每一个点往后走A会去哪里,B会去哪里.而且还必须O(nlogn)给它跑出 ...

  6. [Sdoi2014]旅行 题解

    题目大意: 给出一个n个点的数,和m次操作.每个点有颜色和权值. 每次操作分4种 1:修改一个点的颜色 2:修改一个点的权值 3:询问从x到y的路径上,和x相同颜色的点的权值和(保证x,y同颜色) 4 ...

  7. 洛谷P5022 旅行 题解

    前面几个代码都是部分分代码,最后一个才是AC了的,所以最后一个有详细注释 安利一发自己的Blog 这是提高组真题,233有点欧拉回路的感觉. 题目大意: 一个 连通 图,双向边 ,无重边 , 访问图中 ...

  8. 洛谷P3313 [SDOI2014]旅行 题解 树链剖分+线段树动态开点

    题目链接:https://www.luogu.org/problem/P3313 这道题目就是树链剖分+线段树动态开点. 然后做这道题目之前我们先来看一道不考虑树链剖分之后完全相同的线段树动态开点的题 ...

  9. 洛谷P5022 旅行 题解 去环/搜索

    题目链接:https://www.luogu.org/problem/P5022 这道题目一开始看的时候没有思路,但是看到数据范围里面有一个: \(m = n-1\) 或 \(m = n\) ,一下子 ...

  10. luogu P4842 城市旅行

    嘟嘟嘟 好题,好题 刚开始突发奇想写了一个\(O(n ^ 2)\)暴力,结果竟然过了?!后来才知道是上传题的人把单个数据点开成了10s-- 不过不得不说我这暴力写的挺好看的.删边模仿链表删边,加边的时 ...

随机推荐

  1. kafka 基础入门

    kafka是什么 Kafka (Apache kafka is a distributed streaming platform) ,官方定义是一个分布式流式计算平台.在我开发的项目中,是把kafka ...

  2. CF1370C题解

    本蒟蒻的第二篇题解,找题归功于教练 题目传送门 这道题目找好了规律很简单: 具体思路: 题目大意: 有一个正整数 nnn.两名玩家轮流操作.每次操作可以执行以下一种: 将 nnn 除以一个 nnn 的 ...

  3. Multisim14.0安装包免费获取,超详细中文安装步骤助你快速上手!

    Multisim14.0简介 Multisim14.0是由美国国家仪器公司(NI)推出的专业电子设计自动化](EDA)工具,广泛应用于电路设计.仿真验证.教学实验及科研开发领域.其核心功能是通过虚拟仿 ...

  4. Docker开启远程守护进程访问

    默认情况下,Docker守护进程监听Unix套接字上的连接,以接受来自本地客户端的请求.通过将Docker配置为侦听IP地址和端口以及Unix套接字,可以允许Docker接受来自远程主机的请求.有关此 ...

  5. 【MOOC】华中科技大学计算机组成原理慕课答案-第三章-运算方法与运算器

    待整理. 单选 1 原码除法是指 A. 操作数用绝对值表示,加上符号位后相除 √B. 操作数取绝对值相除,符号位单独处理 C. 操作数用原码表示,然后相除 D. 操作数用补码表示并进行除法,但商用原码 ...

  6. 算法:请找出数组a所有重复元素和比较数组a和数组b得到不重复的新数组和比较数组a和数组b请找出所有重复元素

    /** * 1.给定数组int[] a,int[] b * (1)请找出数组a所有重复元素,例:int[] a = {1,2,3,4,8,9,3,5,1,3},结果int[] a1 = {1,1,3, ...

  7. C++ STL vector预分配空间——resize和reserve

    vector的resize:既分配了空间,也创建了对象,会调用构造函数 vector的reserve:reserve()表示容器预留空间,但不是真正的创建对象,需要通过insert()或push_ba ...

  8. ASP.NET Core 之路由相关

    ASP.NET Core中路由的过程:routing middleware把传入的url与一系列模板进行比对,选择相应的endpoint handler,并将其记录在HttpContext上的requ ...

  9. MySQL的表空间释放

    概述 最近为了对 MySQL 数据库磁盘占用瘦身,对一张近100GB表的历史数据进行了 delete 删除,删除了约2/3的数据,删除后发现该表占用的空间并未减少.通过下面语句查看该表的磁盘占用情况: ...

  10. 用QT、QImage来制作简单图像处理工具

    用QT.QImage来制作简单图像处理工具 源码地址: https://github.com/dependon/simple-image-filter 下载地址(windows版本) github 下 ...