题解 P1351 【联合权值】
先无良宣传一下博客 \(wwwwww\)
文章列表 - 地灵殿 - 洛谷博客
知识点: 树上问题 , \(dfs\)
原题面: P1351 联合权值
题目要求 :
给定一棵树 ,
求所有距离 \(2\) 的点对
权值乘积的最大值 和 权值乘积的和
分析题意 :
可以发现一些 比较浅显 的 性质 :
满足题目条件的 点对 ,- 在一条链上,且深度相差 \(2\) ,
即一个点是另一个点的 \(2\) 级祖先 ,
即 "父亲的父亲" ;
- 是同一个结点 的 子结点
- 在一条链上,且深度相差 \(2\) ,
暴力开写 :
通过上述性质 , 只需要进行一次 \(dfs\) ,
就可以找到所有 点对- 获得每个点的父亲
- 将一个点与其 父亲的父亲 ,
即其二级祖先, 进行匹配 - 枚举一个点的出边时 ,
将出边端点两两匹配 .
求得匹配的点对的权值之积后 , 再进行处理即可 .
直接按照上述过程 写出代码 ,
取得了 \(70\) 分的好成绩 , \(T\) 掉了 \(3\) 个点 .
考虑优化 :
发现 \(dfs\) 中,枚举出边端点 , 并进行两两匹配,
总复杂度是 \(O(n^2)\) 级别的 , 必然会被卡掉 .
但是 两两匹配过程 又不可缺少
考虑优化 这 必要的一步.考虑 某结点的所有子结点 ,
可以发现 :
\(\text{其 最大权值积} = \text{最大子结点权值} \times \text{次大子结点权值}\)
\(\text{其 权值和} = \sum(\text{每个子结点权值} \times \text{其他各子结点的权值和})\)考虑预处理出 每个结点的 最大子结点权值 , 次大子节点权值 , 子结点权值和
只需要一次 \(O(n)\) 的 \(dfs\) 即可通过上述优化 , 已经可以 \(A\) 掉本题了
继续优化
(其实是因为不会预处理次大值)
真的有必要处理出 次大子节点权值吗?
发现根本不需要 , 只需要处理出最大子节点权值即可当枚举到次大子节点时 , 将其与 最大子节点权值相乘,
即可得到 \(u\) 的子节点的最大权值积同时注意判重 , 不能使 \(\ \text{最大点}\times\text{最大点}\) ,
可以通过记录 最大点 的编号来实现.
一些小技巧:
由于没有边权值 ,
只需要知道每一条边的起点终点即可
所以 可以使用 \(vector\) 存边 .
易于枚举匹配 , 还节省空间
附 \(AC\) 代码 :
#include<cstdio>
#include<ctype.h>
#include<vector>
#define max(a,b) ((a)>(b)?(a):(b))
const int MARX = 2e5+10;
const int mod = 1e4+7;
//=============================================================
std::vector <int> e[MARX];
int ans1,ans2,n,w[MARX];
int num,head[MARX],father[MARX];
int maxw[MARX],sumw[MARX],maxv[MARX]; //分存:i的直接子节点 的 最大权值,权值和,最大权值点的编号
//=============================================================
inline int read()
{
int s=1, w=0; char ch=getchar();
for(; !isdigit(ch);ch=getchar()) if(ch=='-') s =-1;
for(; isdigit(ch);ch=getchar()) w = w*10+ch-'0';
return s*w;
}
void dfs1(int u,int fa)//预处理出 u的子节点 的最大权值 和 权值和
{
for(int i=0;i<e[u].size();i++)//枚举每一个出点
if(e[u][i]!=fa)
{
if(w[e[u][i]] >maxw[u]) maxw[u]=w[e[u][i]] , maxv[u]=e[u][i];//预处理最大权值
sumw[u]=(sumw[u]+w[e[u][i]])%mod;//预处理权值和
dfs1(e[u][i],u);//递归进行
}
}
void dfs2(int u,int fa)
{
father[u]=fa;//记录父亲
if(father[father[u]]) //如果存在 二级祖先
ans1=max(ans1,w[u]*w[father[father[u]]]),//进行配对
ans2=( ans2 + 2*w[u]*w[father[father[u]]] )%mod;
for(int i=0;i<e[u].size();i++)//枚举直接子结点
if(e[u][i]!=fa)
{
if(e[u][i]!=maxv[u]) ans1=max(ans1,w[e[u][i]]*maxw[u]);//如果当前点不为最大点
ans2=( ans2 + w[e[u][i]]*(sumw[u]-w[e[u][i]]) )%mod;//增加权值和
dfs2(e[u][i],u);
}
}
//=============================================================
signed main()
{
n=read();
for(int i=1;i<n;i++) //vector 建图
{
int u=read(),v=read();
e[u].push_back(v);
e[v].push_back(u);
}
for(int i=1;i<=n;i++) w[i]=read();
dfs1(1,0);//预处理
dfs2(1,0);
printf("%d %d",ans1,ans2%mod);
}
原文地址:https://www.cnblogs.com/luckyblock/p/11456272.html
- 编辑-发布-开发分离:git作为NoSQL数据库
- 性能优化总结(四):预加载的设计
- 在Expression Blend中使用XAML建立3D应用程序
- 使用 Asp.net Future May 2007 开发Silverlight应用
- Rafy 领域实体框架设计 - 重构 ORM 中的 Sql 生成
- RePractise前端篇: 前端演进史
- 性能优化总结(三):聚合SQL在GIX4中的应用
- ASP.NET AJAX 控件开发基础
- 听我说说我的博客: 月访问量过万的个人IT博客的技术史
- TransactionScope和Enterprise Libray 3.0 Data Access Application Block
- 《Python Web开发 - 测试驱动方法》阅后感
- 微信小程序分享——会话服务器和业务服务器合并
- 微信官方开源UI库-WeUI
- ViewFlipper实现多页面切换
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法