传送门

题目大意:

一颗n个点的树,给出m条链,第i条链的权值是\(w_i\),可以选择若干条不相交的链,求最大权值和。

题目分析:

树型dp: dp[u][0]表示不经过u节点,其子树的最优值,dp[u][1]表示考虑经过u节点该子树的最优值(可能过,可能不过),很明显:$$dp[u][0] = \sum{max(dp[v][0], dp[v][1])} v是u的儿子$$, 下面来算dp[u][1]: 考虑一条经过u(以u为lca)的链,他经过子树中的节点v(可能有多个),那么$$dp[u][1] = dp[u][0] + w_i + max{-max(dp[v][0], dp[v][1]) + dp[v][0]}$$减去max(dp[v][0], dp[v][1])是因为我们更新dp[u][0]时取得是两者较大值,而此时需要减去的其实是dp[v][1],如果取较大值减去了dp[v][0],然后加上dp[v][0]就等于没减,没有影响,而若减去dp[v][1],然后加上dp[v][0],则刚好达到目的。

现在来考虑怎么求该链上的dp值:有两种方法

  • 树链剖分 + 线段树 + dp: 链剖以便求lca和区间求和,在lca节点放入这条链,扫描完子树后(dfs子树完便得到dp[u][0]),处理以该节点u为lca的链x->y,将链拆成两条:x->u和u->y,另tmp = dp[u][0],\(tmp -= queryMaxDp0Dp1Sum(x, u), tmp += queryDp0Sum(x, u)\) 另一条链同理,处理完后,dp[u][1] = max(dp[u][1], tmp + \(w_i\));

    处理完这些链后,将u节点的dp值插入链剖线段树中,并更新答案。总复杂度为\(n log^2n\)

  • 树状数组 + dp: 会快一点,但不会。

code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<set>
#include<cmath>
using namespace std; namespace IO{
inline int read(){
int i = 0, f = 1; char ch = getchar();
for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar());
if(ch == '-') f = -1, ch = getchar();
for(; ch >= '0' && ch <= '9'; ch = getchar()) i = (i << 3) + (i << 1) + (ch - '0');
return i * f;
}
inline void wr(int x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) wr(x / 10);
putchar(x % 10 + '0');
}
}using namespace IO; const int N = 1e5 + 5, M = 1e5 + 5, OO = 0x3f3f3f3f;
int n, m, top[N], son[N], pos[N], tot, dep[N], fa[N], sze[N];
vector<int> G[N];
typedef long long ll;
ll ans, dp[N][2];
struct node{int u, v; ll val;};
vector<node> chainThrough[N]; namespace SegTree{
ll sum[N << 2], maxSum[N << 2];
inline void insert(int k, int l, int r, int pos, ll v, ll *t){
if(l == r){t[k] = v; return;}
int mid = (l + r) >> 1;
if(pos <= mid) insert(k << 1, l, mid, pos, v, t);
else insert(k << 1 | 1, mid + 1, r, pos, v, t);
t[k] = t[k << 1] + t[k << 1 | 1];
}
inline ll query(int k, int l, int r, int x, int y, ll *t){
if(x == l && r == y) return t[k];
int mid = (l + r) >> 1;
ll ret = 0;
// if(x <= mid) ret += query(k << 1, l, mid, x, y, t);
// if(y > mid) ret += query(k << 1 | 1, mid + 1, r, x, y, t);
// return ret;
if(y <= mid) return query(k << 1, l, mid, x, y, t);
else if(x > mid) return query(k << 1 | 1, mid + 1, r, x, y, t);
else return query(k << 1, l, mid, x, mid, t) + query(k << 1 | 1, mid + 1, r, mid + 1, y, t);
}
}using namespace SegTree; inline void dfs1(int u, int f){
dep[u] = dep[f] + 1, fa[u] = f, sze[u] = 1;
for(int e = G[u].size() - 1; e >= 0; e--){
int v = G[u][e];
if(v == f) continue;
dfs1(v, u), sze[u] += sze[v];
if(sze[v] > sze[son[u]]) son[u] = v;
}
} inline void dfs2(int u, int f){
if(son[u]){
pos[son[u]] = ++tot;
top[son[u]] = top[u];
dfs2(son[u], u);
}
for(int e = G[u].size() - 1; e >= 0; e--){
int v = G[u][e];
if(v == f || v == son[u]) continue;
pos[v] = ++tot;
top[v] = v;
dfs2(v, u);
}
} inline int getLca(int u, int v){
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u, v);
u = fa[top[u]];
}
return dep[u] < dep[v] ? u : v;
} inline ll pathQuery(int u, int v, ll *t){
ll ret = 0;
while(top[u] != top[v]){
if(dep[top[u]] < dep[top[v]]) swap(u, v);
ret += query(1, 1, n, pos[top[u]], pos[u], t);
u = fa[top[u]];
}
if(dep[u] > dep[v]) swap(u, v);
return ret + query(1, 1, n, pos[u], pos[v], t);
} inline void DP(int u, int f){
for(int e = G[u].size() - 1; e >= 0; e--){
int v = G[u][e];
if(v == f) continue;
DP(v, u), dp[u][0] += max(dp[v][0], dp[v][1]);
}
for(int i = 0; i < chainThrough[u].size(); i++){
int x = chainThrough[u][i].u, y = chainThrough[u][i].v;
ll tmp = dp[u][0];
if(dep[x] > dep[u]) tmp += pathQuery(x, u, sum), tmp -= pathQuery(x, u, maxSum);
if(dep[y] > dep[u]) tmp += pathQuery(y, u, sum), tmp -= pathQuery(y, u, maxSum);
dp[u][1] = max(dp[u][1], tmp + chainThrough[u][i].val);
}
insert(1, 1, n, pos[u], dp[u][0], sum);
insert(1, 1, n, pos[u], max(dp[u][1], dp[u][0]), maxSum);
ans = max(ans, max(dp[u][0], dp[u][1]));
} inline void splitTree(){
tot = 1, pos[1] = 1, top[1] = 1;
dfs1(1, 0), dfs2(1, 0);
} int T; int main(){
T = read();
while(T--){
n = read(), m = read();
for(int i = 1; i <= n; i++) G[i].clear(), chainThrough[i].clear(), ans = 0;
memset(sze, 0, sizeof sze), memset(dep, 0, sizeof dep), memset(son, 0, sizeof son);
memset(sum, 0, sizeof sum), memset(maxSum, 0, sizeof maxSum), memset(dp, 0, sizeof dp);
for(int i = 1; i < n; i++){int u = read(), v = read(); G[u].push_back(v), G[v].push_back(u);}
splitTree();
for(int i = 1; i <= m; i++){int u = read(), v = read(); ll val = read()*1ll; chainThrough[getLca(u, v)].push_back((node){u, v, val});}
DP(1, 0);
// for(int i=1;i<=n;i++)for(int j=0;j<chainThrough[i].size();j++)cout<<i<<" "<<chainThrough[i][j].u<<" "<<chainThrough[i][j].v<<" "<<endl;
wr(ans), putchar('\n');
}
return 0;
}

HDU 5293 Train chain Problem - 树链剖分(树状数组) + 线段树+ 树型dp的更多相关文章

  1. [HDU 5293]Tree chain problem(树形dp+树链剖分)

    [HDU 5293]Tree chain problem(树形dp+树链剖分) 题面 在一棵树中,给出若干条链和链的权值,求选取不相交的链使得权值和最大. 分析 考虑树形dp,dp[x]表示以x为子树 ...

  2. 【bzoj4999】This Problem Is Too Simple! 树链剖分+动态开点线段树

    题目描述 给您一颗树,每个节点有个初始值. 现在支持以下两种操作: 1. C i x(0<=x<2^31) 表示将i节点的值改为x. 2. Q i j x(0<=x<2^31) ...

  3. Luogu 2590 [ZJOI2008]树的统计 / HYSBZ 1036 [ZJOI2008]树的统计Count (树链剖分,LCA,线段树)

    Luogu 2590 [ZJOI2008]树的统计 / HYSBZ 1036 [ZJOI2008]树的统计Count (树链剖分,LCA,线段树) Description 一棵树上有n个节点,编号分别 ...

  4. Qtree3题解(树链剖分(伪)+线段树+set)

    外话:最近洛谷加了好多好题啊...原题入口 这题好像是SPOJ的题,挺不错的.看没有题解还是来一篇... 题意: 很明显吧.. 题解: 我的做法十分的暴力:树链剖分(伪)+线段树+\(set\)... ...

  5. (中等) HDU 5293 Tree chain problem,树链剖分+树形DP。

    Problem Description   Coco has a tree, whose vertices are conveniently labeled by 1,2,…,n.There are ...

  6. [bzoj 3531][SDOI2014]旅行(树链剖分+动态开点线段树)

    题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3531 分析: 对于每个颜色(颜色<=10^5)都建立一颗线段树 什么!那么不是M ...

  7. 洛谷P3313 [SDOI2014]旅行(树链剖分 动态开节点线段树)

    题意 题目链接 Sol 树链剖分板子 + 动态开节点线段树板子 #include<bits/stdc++.h> #define Pair pair<int, int> #def ...

  8. 树链剖分 - Luogu 3384【模板】树链剖分

    [模板]树链剖分 题目描述 已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操 ...

  9. 刷题总结——骑士的旅行(bzoj4336 树链剖分套权值线段树)

    题目: Description 在一片古老的土地上,有一个繁荣的文明. 这片大地几乎被森林覆盖,有N座城坐落其中.巧合的是,这N座城由恰好N-1条双 向道路连接起来,使得任意两座城都是连通的.也就是说 ...

随机推荐

  1. 福昕pdf阅读器如何删除所有注释

    然后选中第一个 移动到最后按住shift,选择最后一个, 总之就是选中所有的 然后右键,点击删除即可. 不要忘记保存呦

  2. 使用Tomcat发布war包

    第一步:下载tomacat 1.下载地址:http://tomcat.apache.org 2.解压后目录如下 3.双击bin文件夹下startup.bat 即可启动tomcat, 计算机会弹出控制台 ...

  3. 如何使用 PyCharm 将代码上传到GitHub上(详细图解)

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 一丶说明 测试条件:需要有GitHub账号以及在本地安装了Git工具,无论是Linux环境还是Windows都是一样的 如果还没有 ...

  4. thinkphp模拟请求和参数绑定

    thinkphp模拟请求和参数绑定 一.总结 1.网页传过来的参数是可以修改的:get或者post等方式 传过来的参数是可以修改的  dump($request->get(['id'=>2 ...

  5. 【Codeforces Round #440 (Div. 2) C】 Maximum splitting

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 肯定用尽量多的4最好. 然后对4取模的结果 为0,1,2,3分类讨论即可 [代码] #include <bits/stdc++ ...

  6. Netty原理和使用

    性能主题 Netty原理和使用 Netty是一个高性能 事件驱动的异步的非堵塞的IO(NIO)框架,用于建立TCP等底层的连接,基于Netty可以建立高性能的Http服务器.支持HTTP. WebSo ...

  7. 15.1 linux操作系统下nand flash驱动框架2

    当我们需要在操作系统上读写普通文件的时候,总是需要一层层往下,最终到达硬件相关操作,当然底层设备大多数都是块设备 NAND FLASH就作为一个最底层的块设备. 而写驱动,就是要构建硬件与操作系统之间 ...

  8. [Angular2] @Ngrx/store and @Ngrx/effects learning note

    Just sharing the learning experience related to @ngrx/store and @ngrx/effects. In my personal opinio ...

  9. 简单的Java多线程的使用

    前几天做一个功能.就是在前台更改信息后会自己主动发邮件给其它的人,相关信息已更改,刚開始是直接在更改信息代码后面增加发送邮件的代码,但发现这样会使界面特别慢,而慢的主要原因是因为发送邮件有时会耗时非常 ...

  10. 仿凤凰时时彩代购平台源代码[ASP+MSSQL]完整下载

    源代码简单介绍 : 适用范围:  时时彩源代码,时时彩程序,开奖平台源代码,投注平台源代码,仿凤凰时时彩源代码 执行环境:  ASP+MSSQL 其它说明:仿凤凰时时彩代购平台源代码.网上售价8000 ...