题意:有两个小孩玩游戏,每个小孩可以选择一个起始点,并且下一个选择的点必须和自己选择的上一个点相邻,问两个选的点权和的最大值是多少?

思路:首先这个问题可以转化为求树上两不相交路径的点权和的最大值,对于这种问题,我们有两种想法:

1:树的直径,受之前HDU多校的那道题的启发,我们先找出树的直径,然后枚举保留直径的哪些部分,去找保留这一部分的最优解,去更新答案。

代码:

#include <bits/stdc++.h>
#define INF 1e18
#define LL long long
using namespace std;
const int maxn = 100010;
vector<int> G[maxn];
LL tot;
int now, f[maxn];
LL d[maxn], val[maxn], sum[maxn];
bool v[maxn], v1[maxn];
LL mx[maxn];
void add(int x, int y) {
G[x].push_back(y);
G[y].push_back(x);
}
void dfs(int x, int fa, LL sum) {
sum += val[x];
v1[x] = 1;
f[x] = fa;
if(sum > tot) {
now = x;
tot = sum;
}
for (auto y : G[x]) {
if(y == fa || v[y]) continue;
dfs(y, x, sum);
}
}
void dfs1(int x, int fa) {
d[x] = val[x];
LL tmp = 0;
for (auto y : G[x]) {
if(y == fa || v[y]) continue;
dfs1(y, x);
tmp = max(tmp, d[y]);
}
d[x] += tmp;
return;
}
vector<int> a;
int main() {
int n, x, y;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld", &val[i]);
}
for (int i = 1; i < n; i++) {
scanf("%d%d", &x, &y);
add(x, y);
}
LL ans = 0;
tot = 0, now = 0;
dfs(1, 0, 0);
tot = 0;
dfs(now, 0, 0);
for (int i = now; i; i = f[i]) {
a.push_back(i);
sum[a.size()] = sum[a.size() - 1] + val[i];
v[i] = 1;
}
for (auto y : a) {
dfs1(y, 0);
}
memset(v1, 0, sizeof(v1));
for (int i = 1; i <= n; i++) {
if(v[i] == 1 || v1[i] == 1) continue;
tot = now = 0;
dfs(i, 0, 0);
tot = 0;
dfs(now, 0, 0);
ans = max(ans, tot + sum[a.size()]);
}
mx[a[a.size() - 1]] = d[a[a.size() - 1]];
for (int i = a.size() - 2; i >= 1; i--) {
mx[a[i]] = max(mx[a[i + 1]], sum[a.size()] - sum[i] + d[a[i]] - val[a[i]]);
}
for (int i = 0; i < a.size() - 1; i++) {
ans = max(ans, d[a[i]] + sum[i] + mx[a[i + 1]]);
}
printf("%lld\n", ans);
}

思路2:树形DP,我们对于每个点保留从它到叶子节点的最长路径和次长路径,以及以它为根的子树中的最长路径。dp完之后,我们对于每个节点,枚举选哪棵子树中的节点作为一条路径,然后去寻找另一条最长路径。可以用前缀最大值和后缀最大值去优化,以及需要注意需要考虑父节点方向对答案的影响。

代码:

#include <bits/stdc++.h>
#define LL long long
#define pll pair<LL, LL>
using namespace std;
const int maxn = 100010;
vector<int> G[maxn];
LL mx_path[maxn], mx_dis[maxn];
LL lmx_path[maxn], rmx_path[maxn];
pll lmx_dis[maxn], rmx_dis[maxn];
LL val[maxn];
LL ans = 0;
void add(int x, int y) {
G[x].push_back(y);
G[y].push_back(x);
}
void dfs(int x, int fa) {
vector<LL> tmp(3);
for (auto y : G[x]) {
if(y == fa) continue;
dfs(y, x);
tmp[0] = mx_dis[y];
sort(tmp.begin(), tmp.end());
mx_path[x] = max(mx_path[y], mx_path[x]);
}
mx_dis[x] = tmp[2] + val[x];
mx_path[x] = max(mx_path[x], tmp[1] + tmp[2] + val[x]);
return;
}
int tot, a[maxn];
struct node {
int x, fa;
LL mx_p, mx_d;
};
queue<node> q;
void solve() {
q.push((node){1, 0, 0, 0});
while(q.size()) {
node tmp = q.front();
q.pop();
tot = 0;
int x = tmp.x, fa = tmp.fa;
tot = 0;
for (int i = 0; i < G[x].size(); i++) {
if(G[x][i] == fa) continue;
a[++tot] = G[x][i];
}
rmx_path[tot + 1] = rmx_path[tot + 2] = 0;
rmx_dis[tot + 1] = rmx_dis[tot + 2] = make_pair(0, 0);
for (int i = 1; i <= tot; i++) {
lmx_path[i] = max(lmx_path[i - 1], mx_path[a[i]]);
lmx_dis[i] = lmx_dis[i - 1];
if(mx_dis[a[i]] >= lmx_dis[i].first) {
lmx_dis[i].second = lmx_dis[i].first;
lmx_dis[i].first = mx_dis[a[i]];
} else if(mx_dis[a[i]] > lmx_dis[i].second) {
lmx_dis[i].second = mx_dis[a[i]];
}
}
for (int i = tot; i >= 1; i--) {
rmx_path[i] = max(rmx_path[i + 1], mx_path[a[i]]);
rmx_dis[i] = rmx_dis[i + 1];
if(mx_dis[a[i]] >= rmx_dis[i].first) {
rmx_dis[i].second = rmx_dis[i].first;
rmx_dis[i].first = mx_dis[a[i]];
} else if(mx_dis[a[i]] > rmx_dis[i].second) {
rmx_dis[i].second = mx_dis[a[i]];
}
}
LL tmp1 = 0;
// printf("%d\n", x);
// for (int i = 1; i <= tot; i++) {
// printf("%lld %lld\n", lmx_path[i], rmx_path[i]);
// }
for (int i = 1; i <= tot; i++) {
LL tmp2 = 0;
tmp2 = max(tmp2, lmx_path[i - 1]);
tmp2 = max(tmp2, rmx_path[i + 1]);
tmp2 = max(tmp2, tmp.mx_p);
tmp2 = max(tmp2, val[x] + lmx_dis[i - 1].first + tmp.mx_d);
tmp2 = max(tmp2, val[x] + rmx_dis[i + 1].first + tmp.mx_d);
tmp2 = max(tmp2, val[x] + lmx_dis[i - 1].first + lmx_dis[i - 1].second);
tmp2 = max(tmp2, val[x] + rmx_dis[i + 1].first + rmx_dis[i + 1].second);
tmp2 = max(tmp2, val[x] + lmx_dis[i - 1].first + rmx_dis[i + 1].first);
tmp2 = max(tmp2, val[x] + tmp.mx_d + max(lmx_dis[i - 1].first, rmx_dis[i + 1].first));
// printf("%lld ", tmp.mx_d);
// printf("%lld ", tmp2);
q.push((node){a[i], x, tmp2, val[x] + max(max(lmx_dis[i - 1].first, rmx_dis[i + 1].first), tmp.mx_d)});
tmp2 += mx_path[a[i]];
// printf("%lld\n", tmp2);
tmp1 = max(tmp1, tmp2);
}
// printf("\n");
ans = max(ans, tmp1);
ans = max(ans, tmp.mx_p + mx_path[x]);
}
}
int main() {
int n, x, y;
// freopen("633Fin.txt", "r" , stdin);
// freopen("out1.txt", "w", stdout);
scanf("%d" , &n);
for (int i = 1; i <= n; i++)
scanf("%lld", &val[i]);
for (int i = 1; i < n; i++) {
scanf("%d%d", &x, &y);
add(x, y);
}
dfs(1, 0);
solve();
printf("%lld\n", ans);
}

Codeforces 633F 树的直径/树形DP的更多相关文章

  1. 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分

    树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...

  2. 2014 Super Training #9 E Destroy --树的直径+树形DP

    原题: ZOJ 3684 http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3684 题意: 给你一棵树,树的根是树的中心(到其 ...

  3. [10.12模拟赛] 老大 (二分/树的直径/树形dp)

    [10.12模拟赛] 老大 题目描述 因为 OB 今年拿下 4 块金牌,学校赞助扩建劳模办公室为劳模办公室群,为了体现 OI 的特色,办公室群被设计成了树形(n 个点 n − 1 条边的无向连通图), ...

  4. HDU4514 湫湫系列故事——设计风景线 ——树的直径/树形dp+判环

    中文题面,给出一个图,问能不能成环,如果可以就输出YES.否则输出该树的直径. 这里的判环我们用路径压缩的并查集就能很快的判断出来,可以在输入的同时进行判断.这题重点就是求树的直径. 树直径的性质可以 ...

  5. Codeforces 633F The Chocolate Spree 树形dp

    The Chocolate Spree 对拍拍了半天才知道哪里写错了.. dp[ i ][ j ][ k ]表示在 i 这棵子树中有 j 条链, 是否有链延伸上来. #include<bits/ ...

  6. POJ 1849 Two(树的直径--树形DP)(好题)

    大致题意:在某个点派出两个点去遍历全部的边,花费为边的权值,求最少的花费 思路:这题关键好在这个模型和最长路模型之间的转换.能够转换得到,全部边遍历了两遍的总花费减去最长路的花费就是本题的答案,要思考 ...

  7. codeforces 161D Distance in Tree 树形dp

    题目链接: http://codeforces.com/contest/161/problem/D D. Distance in Tree time limit per test 3 secondsm ...

  8. codeforces 337D Book of Evil (树形dp)

    题目链接:http://codeforces.com/problemset/problem/337/D 参考博客:http://www.cnblogs.com/chanme/p/3265913 题目大 ...

  9. Codeforces 543D Road Improvement(树形DP + 乘法逆元)

    题目大概说给一棵树,树的边一开始都是损坏的,要修复一些边,修复完后要满足各个点到根的路径上最多只有一条坏的边,现在以各个点为根分别求出修复边的方案数,其结果模1000000007. 不难联想到这题和H ...

随机推荐

  1. JavaWeb(四):JDBC

    数据持久化(persistence) 把数据保存到可掉电式存储设备中以供之后使用. 大多数情况下,特别是企业级应用,数据持久化意味着将内存中的数据保存到硬盘上加以”固化”,而持久化的实现过程大多通过各 ...

  2. Codeforces 512B: Fox And Jumping

    题目链接 题意说的是,有n种卡片,使用第i种卡片可以使当前自己在数轴上的位置移动 l[i],要获得使用第i种卡片的代价是 c[i],求能使自己移动到数轴上任意位置的最小代价,如果不可能则输出-1 当前 ...

  3. 【leetcode】1052. Grumpy Bookstore Owner

    题目如下: Today, the bookstore owner has a store open for customers.length minutes.  Every minute, some ...

  4. 【leetcode】885. Boats to Save People

    题目如下: 解题思路:本题可以采用贪心算法,因为每条船最多只能坐两人,所以在选定其中一人的情况下,再选择第二个人使得两人的体重最接近limit.考虑到人的总数最大是50000,而每个人的体重最大是30 ...

  5. 一些vue 响应式系统的底层的细节

    当你把一个普通的 JavaScript 对象传给 Vue 实例的 data 选项,Vue 将遍历此对象所有的属性,并使用 Object.defineProperty 把这些属性全部转为 getter/ ...

  6. Oracle12c修改时区

    Oacle12c支持可插入数据库(PDB)在一个统一的数据库(CDB)中具有不同的字符集.时区文件版本和数据库时区. 出于性能原因,Oracle建议将数据库时区设置为UTC(0:00),因为不需要转换 ...

  7. C#中命名空间,C#程序中的一种代码组织形式,主要用来标识类的可以范围,引用using 命名空间

    C# C#中命名空间,C#程序中的一种代码组织形式,主要用来标识类的可以范围 use system; use system.collect.core; namespace sss{ } using 命 ...

  8. 退役——halfrot's life in OI

    这是一个没有人看的博客里丢了两年的坑,还有很多事应该做,但是我很懒,所以今天把它填了. 前记:和很多人的竞赛生涯一样,一开始我也是奋不顾身,奔月而去,然而身处弱校,没有人引导方向,再加上自己很蒻的主要 ...

  9. LRU management

    LRU management 字典树用来查找值,实现map<string,int>操作 tips:tot必须从一开始QAQ #include<bits/stdc++.h> us ...

  10. 攻防世界 | hello_pwn

    看样子是要让我们通过read(0, &unk_601068, 0x10uLL),读入 unk_601068 将 dword_60106C 覆盖 6c-68=4,所以: from pwn imp ...