2019年杭电多校第三场 1011题Squrirrel(HDU6613+树DP)

题目链接

传送门

题意

给你一棵无根树,要你寻找一个根节点使得在将一条边权变为\(0\)后,离树根最远的点到根节点的距离最小。

思路

本题和求树的直径很像,不过要记得的东西有点多,且状态也很多。

\(fi[u][0]\)表示在\(u\)这个结点不删边沿着子树方向能到达的最远距离,\(se[u][0]\)为第二远,\(th[u][0]\)为第三远,\(fa[u][0]\)表示沿着父亲方向能到达的最远距离,第二维为\(1\)表示删一条边能到达的距离。

不删边的转移和求树的直径转移方程基本上是一样的,就不再说明。

首先从以\(u\)为根的子树中删除一条边,能到达的最远距离的最小值为\(fi[u][1]=max(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;
}

原文地址:https://www.cnblogs.com/Dillonh/p/11269111.html

时间: 2024-10-09 05:42:35

2019年杭电多校第三场 1011题Squrirrel(HDU6613+树DP)的相关文章

2019 杭电多校 第三场

2019 Multi-University Training Contest 3 补题链接:2019 Multi-University Training Contest 3 1002 Blow up the city (HDU-6604) 题意 给定 \(n\) 个点和 \(m\) 条边的有向无环图,给出 \(q\) 次询问,每个询问给出 \(a\) 和 \(b\),求有多少个点,满足该点删去后 \(a\) 和 \(b\) 中至少一个点不能到达出度为 \(0\) 的点. 题解 支配树/灭绝树 拓

2019杭电多校第三场 1004 Distribution of books

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6606 考虑二分答案,我们二分一个值\(x\),那么要怎么来验证这个答案是否可行,考虑dp求解,设\(dp[i]\)为前i个在答案为\(x\)的情况下划分最最多组数,那么若\(dp[n] \geq k\) 则这个x可行, 很显然可以看出\(x\)是单调的,所以二分. \[dp[i] = max(dp[j]) + 1 (sum[i] - sum[j-1] \leq x)\] 如果直接采用暴力枚举的话复杂

2019杭电多校第三场 1008 K-th Closest Distance

题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=6621 考虑主席树,我们先将所有值离散化之后建主席树.对于每个查询\(s,t,p,k\) 我们考虑二分一个值\(mid\),考虑当前区间内,\([p-mid, p+mid]\)的值有多少个,很显然这是符合单调性的,那么我们只需要每次判断即可.时间复杂度\(O(nlog^2n)\) #include <bits/stdc++.h> #define pii pair<int, int> #d

2018 Multi-University Training Contest 3 杭电多校第三场

躺了几天 终于记得来填坑了 1001 Ascending Rating   (hdoj 6319) 链接:http://acm.hdu.edu.cn/showproblem.php?pid=6319 单调队列 具体有点类似双端队列滑动窗口 题意:在一个队列中 每次都给定一个固定长度的区间 从i=1开始向后移动 每次在这个区间中进行a[i]和a[j]的比较 若a[i]<a[j] count++ 最大值更新为a[j] ,每个区间的最大值和count都分别异或i 求出分别的和 求区间最大值可以比较容易

杭电多校第三场 A Ascending Rating

Problem Description Before the start of contest, there are n ICPC contestants waiting in a long queue. They are labeled by 1 to n from left to right. It can be easily found that the i-th contestant's QodeForces rating is ai.Little Q, the coach of Qua

2019杭电多校第六场hdu6638 Snowy Smile(线段树+枚举)

Snowy Smile 题目传送门 解题思路 先把y离散化,然后把点按照x的大小进行排序,我们枚举每一种x作为上边界,然后再枚举其对应的每一种下边界.按照这种顺序插入点,这是一个压维的操作,即在线段树中的y位置加上其w,并利用线段树来更新动态的最大子段和. 代码如下 #include <bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; typedef long long ll; const int N = 2005; stru

HDU 5742 It&#39;s All In The Mind (贪心) 2016杭电多校联合第二场

题目:传送门. 题意:求题目中的公式的最大值,且满足题目中的三个条件. 题解:前两个数越大越好. #include <iostream> #include <algorithm> #include <cstdio> #include <cstring> using namespace std; int gcd(int a,int b) { if(!b) return a; return gcd(b,a%b); } int main() { int t; ci

2019 杭电多校 第五场

2019 Multi-University Training Contest 5 补题链接:2019 Multi-University Training Contest 5 罚时爆炸 自闭场 1004 equation (HDU 6627) 题意: 给定一个整数 \(C\) 和 \(N\) 组 \(a_i,b_i\),求 \(∑_{i=1}^N|a_i\cdot x + b_i| = C\) 的所有解,如果有无穷多个解就输出 -1. 思路 分类讨论 分类讨论去绝对值.根据 \(b_i / a_i

2019杭电多校第三次hdu6609 Find the answer(线段树)

Find the answer 题目传送门 解题思路 要想变0的个数最少,显然是优先把大的变成0.所以离散化,建立一颗权值线段树,维护区间和与区间元素数量,假设至少减去k才能满足条件,查询大于等于k的最少数量即可. 代码如下 #include <bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; typedef long long ll; inline int read(){ int res = 0, w = 0; char c