2019年杭电多校第三场 1011题Squrirrel(HDU6613+树DP)
题目链接
题意
给你一棵无根树,要你寻找一个根节点使得在将一条边权变为\(0\)后,离树根最远的点到根节点的距离最小。
思路
本题和求树的直径很像,不过要记得的东西有点多,且状态也很多。
\(fi[u][0]\)表示在\(u\)这个结点不删边沿着子树方向能到达的最远距离,\(se[u][0]\)为第二远,\(th[u][0]\)为第三远,\(fa[u][0]\)表示沿着父亲方向能到达的最远距离,第二维为\(1\)表示删一条边能到达的距离。
不删边的转移和求树的直径转移方程基本上是一样的,就不再说明。
首先从以\(u\)为根的子树中删除一条边,能到达的最远距离的最小值为\(fi[u][1]=min(fi[v][0],max(fi[v][1],se[v][0])+w)\),\(v\)为\(u\)沿着\(v\)往子树方向走是最远的,也就是说它有三种状态:
- 删除与\(v\)连接的边能到达的最远距离,在\(u\)的所有儿子结点中,你不删与\(v\)相连的边而删其他的边,那么从子树方向能到达的最远距离仍然是最远距离,不会产生任何影响,肯定不是最优解。
- 不删除与\(v\)连接的边而在以\(v\)为根的子树中删除一条边能到达的最远距离,注意删除了最远距离的路径上的一条边后原来的次远距离可能变成了最远距离。
次远距离的最小值一样的转移方法,至于对于\(u\)在删除一条边后\(fi[u][1]\)与\(se[u][0]\)的大小关系我们暂时先不考虑。
上面的转移方程只能处理从子树方向的转移,却不能处理沿着父亲结点方向能到达的最远距离,因此我们需要再进行一次\(dfs\)来进行状态转移。
假设当前已经处理出了父亲结点\(u\)沿着其父亲结点在不删边的情况下不沿着\(v\)这个方向所能到达的最远距离\(fa[u][0]\)和删边的最远距离\(fa[u][1]\),那么子结点\(v\)沿着\(u\)所能到达的最远距离的转移:
- 当\(v\)走是\(u\)沿着子树走能到达的最远距离,那么\(fa[v][0]=max(fa[u][0],se[u][0])+w\),也就是从\(u\)沿着其父亲结点所能到达的最远距离与沿着其子结点所能到达的次远距离两者中最远的转移过来。\(fa[v][1]=min(max(fa[u][0], se[u][0]), min(max(fa[u][1], se[u][0]), max(fa[u][0], max(se[u][1], th[u][0]))) + ed[i].w)\),删除\(u,v\)之间的边所能到达的最远距离与不删除\(u,v\)之间的边所能到达的最远距离,因为删除一条边之后次远距离并不一定比第三远的距离远因此这两者之间也要取个\(max\)。
- 当\(v\)走是\(u\)沿着子树走能到达的次远、第三远的转移方程和原理类似。
代码实现如下
#include <set>
#include <map>
#include <deque>
#include <queue>
#include <stack>
#include <cmath>
#include <ctime>
#include <bitset>
#include <cstdio>
#include <string>
#include <vector>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
typedef long long LL;
typedef pair<LL, LL> pLL;
typedef pair<LL, int> pLi;
typedef pair<int, LL> pil;;
typedef pair<int, int> pii;
typedef unsigned long long uLL;
#define lson rt<<1
#define rson rt<<1|1
#define lowbit(x) x&(-x)
#define name2str(name) (#name)
#define bug printf("*********\n")
#define debug(x) cout<<#x"=["<<x<<"]" <<endl
#define FIN freopen("D://Code//in.txt","r",stdin)
#define IO ios::sync_with_stdio(false),cin.tie(0)
const double eps = 1e-8;
const int mod = 1000000007;
const int maxn = 2e5 + 7;
const double pi = acos(-1);
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3fLL;
int t, n, tot, u, v, w, ans, idx;
int head[maxn], fa[maxn][3], d[maxn];
int idx1[maxn], idx2[maxn], idx3[maxn];
int fi[maxn][3], se[maxn][3], th[maxn][3];
struct edge {
int v, w, next;
}ed[maxn*2];
void add(int u, int v, int w) {
ed[tot].v = v;
ed[tot].w = w;
ed[tot].next = head[u];
head[u] = tot++;
}
void dfs1(int u, int p) {
for(int i = head[u]; ~i; i = ed[i].next) {
int v = ed[i].v;
if(v == p) continue;
dfs1(v, u);
d[v] = ed[i].w;
if(fi[v][0] + ed[i].w > fi[u][0]) {
th[u][0] = se[u][0];
idx3[u] = idx2[u];
se[u][0] = fi[u][0];
idx2[u] = idx1[u];
idx1[u] = v;
fi[u][0] = fi[v][0] + ed[i].w;
} else if(fi[v][0] + ed[i].w > se[u][0]) {
th[u][0] = se[u][0];
idx3[u] = idx2[u];
se[u][0] = fi[v][0] + ed[i].w;
idx2[u] = v;
} else if(fi[v][0] + ed[i].w > th[u][0]) {
th[u][0] = fi[v][0] + ed[i].w;
idx3[u] = v;
}
}
fi[u][1] = min(fi[idx1[u]][0], max(fi[idx1[u]][1], se[idx1[u]][0]) + d[idx1[u]]);
se[u][1] = min(fi[idx2[u]][0], max(fi[idx2[u]][1], se[idx2[u]][0]) + d[idx2[u]]);
}
void dfs2(int u, int p) {
int tmp = min(max(fa[u][1], fi[u][0]), max(fa[u][0], max(fi[u][1], se[u][0])));
if(tmp < ans) ans = tmp, idx = u;
else if(tmp == ans && u < idx) ans = tmp, idx = u;
for(int i = head[u]; ~i; i = ed[i].next) {
int v = ed[i].v;
if(v == p) continue;
if(idx1[u] == v) {
fa[v][0] = max(fa[u][0], se[u][0]) + ed[i].w;
fa[v][1] = min(max(fa[u][0], se[u][0]), min(max(fa[u][1], se[u][0]), max(fa[u][0], max(se[u][1], th[u][0]))) + ed[i].w);
} else {
fa[v][0] = max(fa[u][0], fi[u][0]) + ed[i].w;
if(idx2[u] == v) {
fa[v][1] = min(max(fa[u][0], fi[u][0]), min(max(fa[u][1], fi[u][0]), max(fa[u][0], max(fi[u][1], th[u][0]))) + ed[i].w);
} else {
fa[v][1] = min(max(fa[u][0], fi[u][0]), min(max(fa[u][1], fi[u][0]), max(fa[u][0], max(fi[u][1], se[u][0]))) + ed[i].w);
}
}
dfs2(v, u);
}
}
int main() {
#ifndef ONLINE_JUDGE
FIN;
#endif // ONLINE_JUDGE
scanf("%d", &t);
while(t--) {
scanf("%d", &n);
tot = 0;
for(int i = 1; i <= n; ++i) {
head[i] = -1;
d[i] = idx1[i] = idx2[i] = idx3[i] = 0;
for(int j = 0; j < 2; ++j) {
fi[i][j] = se[i][j] = th[i][j] = fa[i][j] = 0;
}
}
for(int i = 1; i <= n - 1; ++i) {
scanf("%d%d%d", &u, &v, &w);
add(u, v, w);
add(v, u, w);
}
dfs1(1, 0);
ans = inf, idx = 1;
dfs2(1, 0);
printf("%d %d\n", idx, ans);
}
return 0;
}
2019年杭电多校第三场 1011题Squrirrel(HDU6613+树DP)的更多相关文章
- 2019年杭电多校第三场 1008题Game(HDU6610+带修改莫队+Nim博弈)
题目链接 传送门 题意 给你\(n\)堆石子,每堆有\(a_i\)堆石子,\(q\)次操作: 在\([L,R]\)内有多少个子区间使得\(Alice\)(先手)在\(Nim\)博弈中获胜: 交换\(a ...
- Make Rounddog Happy(2019年杭电多校第十场1011+HDU6701+启发式分治)
目录 题目链接 题意 思路 代码 题目链接 传送门 题意 求有多少个子区间满足\(a_l,a_{l+1},\dots,a_r\)均不相同且\(max(a_l,a_{l+1},\dots,a_r)-(r ...
- 可持久化线段树的学习(区间第k大和查询历史版本的数据)(杭电多校赛第二场1011)
以前我们学习了线段树可以知道,线段树的每一个节点都储存的是一段区间,所以线段树可以做简单的区间查询,更改等简单的操作. 而后面再做有些题目,就可能会碰到一种回退的操作.这里的回退是指回到未做各种操作之 ...
- [2019杭电多校第三场][hdu6609]Find the answer(线段树)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6609 大致题意是求出每个位置i最小需要将几个位置j变为0(j<i),使得$\sum_{j=1}^ ...
- [2019杭电多校第三场][hdu6608]Fansblog
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6608 大致题意是比p小的最大素数q,求q!%p的值. 由威尔逊定理开始推: $(p-1)!\equiv ...
- [2019杭电多校第三场][hdu6606]Distribution of books(线段树&&dp)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6606 题意为在n个数中选m(自选)个数,然后把m个数分成k块,使得每块数字之和最大的最小. 求数字和最 ...
- 2019杭电多校第三场hdu6608 Fansblog(威尔逊定理)
Fansblog 题目传送门 解题思路 Q! % P = (P-1)!/(P-1)...(Q-1) % P. 因为P是质数,根据威尔逊定理,(P-1)!%P=P-1.所以答案就是(P-1)((P-1) ...
- 2019杭电多校第三场hdu6609 Find the answer(线段树)
Find the answer 题目传送门 解题思路 要想变0的个数最少,显然是优先把大的变成0.所以离散化,建立一颗权值线段树,维护区间和与区间元素数量,假设至少减去k才能满足条件,查询大于等于k的 ...
- 2019杭电多校第三场hdu6606 Distribution of books(二分答案+dp+权值线段树)
Distribution of books 题目传送门 解题思路 求最大值的最小值,可以想到用二分答案. 对于二分出的每个mid,要找到是否存在前缀可以份为小于等于mid的k份.先求出这n个数的前缀和 ...
随机推荐
- 对post提交数据Content-Type的理解
Content-Type是指http/https发送信息至服务器时的内容编码类型,contentType用于表明发送数据流的类型,服务器根据编码类型使用特定的解析方式,获取数据流中的数据. 在网络请求 ...
- 【操作系统之十】内存分页管理与swap
一.虚拟内存电脑里内存分内存条(这里我们叫物理内存)和硬盘,内存条保存程序运行时数据,硬盘持久保存数据.那么虚拟内存是什么? 程序运行会启动一个进程,进程里有程序段.全局数据.栈和堆,这些都会加载到内 ...
- asbDio (asbDio.dll)
using System;using System.Collections.Generic;using System.Text;using System.Runtime.InteropServices ...
- 页面嵌入iframe关于父子传参调用
1.首先来说一下iframe是干什么用的 IFRAME是HTML标签,作用是文档中的文档,或者浮动的框架(FRAME).iframe元素会创建包含另外一个文档的内联框架(即行内框架),通俗点说就是在一 ...
- 备忘录(Memento)模式
备忘录模式又叫做快照模式或者Token模式. 备忘录对象是一个用来存储另一个对象内部状态的快照的对象.备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捕捉住,并外部化,存储起来,从而可以在将来 ...
- golang知识精要(一)
一.第一章 命令行参数可通过os.Args访问,os.Args是切片 切片遵循左闭右开原则,如sl[1:3]不包含下标为3的元素 for循环两种方式 方式一: for initial; conditi ...
- 删除排序链表中的重复元素II --链表
题目 给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字. 示例1 输入: ->->->->->-> 输出: ->-> ...
- APIO2019简要题解
Luogu P5444 [APIO2019]奇怪装置 看到这种题,我们肯定会想到\((x,y)\)一定有循环 我们要找到循环节的长度 推一下发现\(x\)的循环节长为\(\frac{AB}{B+1}\ ...
- go ---作用域及判断变量类型的方式。
package main import ( "fmt" ) var v = "1, 2, 3" func main() { v := []int{1, 2, 3 ...
- 通过静态发现方式部署 Etcd 集群
在「etcd使用入门」一文中对etcd的基本知识点和安装做了一个简要的介绍,这次我们来说说如何部署一个etcd集群. etcd构建自身高可用集群主要有三种形式: 静态发现: 预先已知etcd集群中有哪 ...