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

题面

在一棵树中,给出若干条链和链的权值,求选取不相交的链使得权值和最大。

分析

考虑树形dp,dp[x]表示以x为子树的最大权值和(选的链都在i的子树中)

设sum[x]表示x的儿子的dp值和,即\(\sum _{y \in \mathrm{son}(x)} dp[y]\)

1.不选两端点lca为x的链,dp[x]=sum[x]

2.选两端点lca为x的链,则dp[x]=max{链的权值+链上节点的所有子节点dp的和 - 链上节点dp和}(因为x的子节点也可能在链上,要去掉),这样选的链一定和lca为x的链不相交。取max即可得到答案。如果不理解,请看下图。

路径为4->7,如果直接对sum求和,我们会把2,3,4,5,6,7都算进答案,但实际上我们只需要5,6,所以再做一次减法,把4->7路径上的点减掉就可以了

预处理的时候可以保存两端点lca为x的链的编号。看到路径求和,我们直接用树链剖分维护即可。从下到上dp,一边更新dp值和sum值,一边求和。

时间复杂度\(O(n\log^2n)\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define maxn 100000
#define maxm 100000
using namespace std;
int t,n,m;
struct edge{
int from;
int to;
int next;
}E[maxn*2+5];
int head[maxn+5];
int esz=1;
void add_edge(int u,int v){
esz++;
E[esz].from=u;
E[esz].to=v;
E[esz].next=head[u];
head[u]=esz;
} struct segment_tree{
struct node{
int l;
int r;
long long sum;
}tree[maxn*4+5];
void push_up(int pos){
tree[pos].sum=tree[pos<<1].sum+tree[pos<<1|1].sum;
}
void build(int l,int r,int pos){
tree[pos].l=l;
tree[pos].r=r;
tree[pos].sum=0;
if(l==r) return;
int mid=(l+r)>>1;
build(l,mid,pos<<1);
build(mid+1,r,pos<<1|1);
push_up(pos);
}
void update(int upos,long long uval,int pos){
if(tree[pos].l==tree[pos].r){
tree[pos].sum+=uval;
return;
}
int mid=(tree[pos].l+tree[pos].r)>>1;
if(upos<=mid) update(upos,uval,pos<<1);
else update(upos,uval,pos<<1|1);
push_up(pos);
}
long long query(int L,int R,int pos){
if(L<=tree[pos].l&&R>=tree[pos].r){
return tree[pos].sum;
}
int mid=(tree[pos].l+tree[pos].r)>>1;
long long ans=0;
if(L<=mid) ans+=query(L,R,pos<<1);
if(R>mid) ans+=query(L,R,pos<<1|1);
return ans;
}
}T1,T2;//T1维护sum,T2维护dp
int tim;
int sz[maxn+5],deep[maxn+5],dfn[maxn+5],top[maxn+5],son[maxn+5],fa[maxn+5];
void dfs1(int x,int f){
fa[x]=f;
sz[x]=1;
deep[x]=deep[f]+1;
for(int i=head[x];i;i=E[i].next){
int y=E[i].to;
if(y!=f){
dfs1(y,x);
sz[x]+=sz[y];
if(sz[y]>sz[son[x]]) son[x]=y;
}
}
}
void dfs2(int x,int t){
top[x]=t;
dfn[x]=++tim;
if(son[x]) dfs2(son[x],t);
for(int i=head[x];i;i=E[i].next){
int y=E[i].to;
if(y!=fa[x]&&y!=son[x]){
dfs2(y,y);
}
}
}
int lca(int x,int y){
while(top[x]!=top[y]){
if(deep[top[x]]>deep[top[y]]) x=fa[top[x]];
else y=fa[top[y]];
}
if(deep[x]<deep[y]) return x;
else return y;
} void update_point(int x,long long uval,segment_tree &T){
T.update(dfn[x],uval,1);
}
long long query_route(int x,int y,segment_tree &T){
int tx=top[x];
int ty=top[y];
long long ans=0;
while(tx!=ty){
if(deep[tx]<deep[ty]){
swap(tx,ty);
swap(x,y);
}
ans+=T.query(dfn[tx],dfn[x],1);
x=fa[tx];
tx=top[x];
}
if(deep[x]>deep[y]) swap(x,y);
ans+=T.query(dfn[x],dfn[y],1);
return ans;
} struct route{
int x;
int y;
int lc;
long long w;
}q[maxm+5];
vector<int>rid[maxn+5];//两端点lca为x的链的编号 long long sum[maxn+5];//sum(dp[son(x)])
long long dp[maxn+5];
void dfs3(int x){//dp过程
sum[x]=0;
for(int i=head[x];i;i=E[i].next){
int y=E[i].to;
if(y!=fa[x]){
dfs3(y);
sum[x]+=dp[y];
}
}
dp[x]=sum[x];
update_point(x,sum[x],T1);//因为下一步就要用到,这里要更新sum[x]
for(int i=0;i<rid[x].size();i++){
int k=rid[x][i];
dp[x]=max(dp[x],q[k].w+query_route(q[k].x,q[k].y,T1)-query_route(q[k].x,q[k].y,T2));
}
update_point(x,dp[x],T2);
} void ini(){
memset(deep,0,sizeof(deep));
memset(dfn,0,sizeof(dfn));
memset(dp,0,sizeof(dp));
memset(fa,0,sizeof(fa));
memset(head,0,sizeof(head));
for(int i=1;i<=n;i++) rid[i].clear();
memset(son,0,sizeof(son));
memset(sum,0,sizeof(sum));
esz=1;
memset(sz,0,sizeof(sz));
tim=0;
memset(top,0,sizeof(top));
T1.build(1,n,1);
T2.build(1,n,1);
}
int main(){
int u,v,w;
scanf("%d",&t);
while(t--){
scanf("%d %d",&n,&m);
ini();
for(int i=1;i<n;i++){
scanf("%d %d",&u,&v);
add_edge(u,v);
add_edge(v,u);
}
dfs1(1,0);
dfs2(1,1);
for(int i=1;i<=m;i++){
scanf("%d %d %lld",&q[i].x,&q[i].y,&q[i].w);
q[i].lc=lca(q[i].x,q[i].y);
rid[q[i].lc].push_back(i);
}
dfs3(1);
printf("%lld\n",dp[1]);
} }

[HDU 5293]Tree chain problem(树形dp+树链剖分)的更多相关文章

  1. HDU 5293 Tree chain problem 树形dp+dfs序+树状数组+LCA

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5293 题意: 给你一些链,每条链都有自己的价值,求不相交不重合的链能够组成的最大价值. 题解: 树形 ...

  2. HDU 5293 Tree chain problem 树形DP

    题意: 给出一棵\(n\)个节点的树和\(m\)条链,每条链有一个权值. 从中选出若干条链,两两不相交,并且使得权值之和最大. 分析: 题解 #include <cstdio> #incl ...

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

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

  4. hdu5293 Tree chain problem 树形dp+线段树

    题目:pid=5293">http://acm.hdu.edu.cn/showproblem.php?pid=5293 在一棵树中,给出若干条链和链的权值.求选取不相交的链使得权值和最 ...

  5. HDU 5293 Tree chain problem

    树状数组 + dp 设$f_i$表示以$i$为根的子树中的能选取的最大和,$sum_x$表示$\sum_{f_y}$  ($y$是$x$的一个儿子),这样子我们把所有给出的链按照两点的$lca$分组, ...

  6. codeforces 671D Roads in Yusland & hdu 5293 Tree chain problem

    dp dp优化 dfs序 线段树 算是一个套路.可以处理在树上取链的问题.

  7. 6.3 省选模拟赛 Decompose 动态dp 树链剖分 set

    LINK:Decompose 看起来很难 实际上也很难 考验选手的dp 树链剖分 矩阵乘法的能力. 容易列出dp方程 暴力dp 期望得分28. 对于链的情况 容易发现dp方程可以转矩阵乘法 然后利用线 ...

  8. HDU 5293 Train chain Problem - 树链剖分(树状数组) + 线段树+ 树型dp

    传送门 题目大意: 一颗n个点的树,给出m条链,第i条链的权值是\(w_i\),可以选择若干条不相交的链,求最大权值和. 题目分析: 树型dp: dp[u][0]表示不经过u节点,其子树的最优值,dp ...

  9. HDU 3966:Aragorn's Story(树链剖分)

    http://acm.hdu.edu.cn/showproblem.php?pid=3966 题意:有n个点n-1条边,每个点有一个权值,有两种操作:询问一个点上权值是多少和修改u到v这条链上的权值. ...

随机推荐

  1. layui在当前页面弹出一个iframe层,并改变这个iframe层里的一些内容

    layer.open({ type: 2, title: "专家信息", area: ['100%', '100%'], content: '/ZhuanJiaKu/AddZhua ...

  2. AQS简介以及源码分析

    参考链接:https://www.jianshu.com/p/da9d051dcc3d 参考链接:https://www.cnblogs.com/waterystone/p/4920797.html

  3. mysql 乐观锁、悲观锁、共享锁,排它锁

    mysql锁机制分为表级锁和行级锁,本文就和大家分享一下我对mysql中行级锁中的共享锁与排他锁进行分享交流. 共享锁又称为读锁,简称S锁,顾名思义,共享锁就是多个事务对于同一数据可以共享一把锁,都能 ...

  4. R语言把DataFrame的一行变成向量

    在R语言里面,DataFrame的一列数据本质上可以认为是一个向量或列表,但是一行数据不是. 今天有一个31列的数据集,由于放在第一行的变量名格式不规范,读入数据的时候不能顺带读入变量名.于是跳过首行 ...

  5. 探讨vue的双向绑定原理及实现

    1.vue的实现原理 vue的双向绑定是由数据劫持结合发布者-订阅者模式实现的,那么什么是数据劫持?vue是如何进行数据劫持的?说白了就是通过Object.defineProperty()来劫持对象属 ...

  6. jmeter接口测试与接口测试工具

    接口测试与接口测试工具 1,什么是接口? 接口,Application Programming Interface(API) 通俗的讲 就是HTTP请求 2,什么是接口测试? 接口测试测试组件间接口的 ...

  7. nginx配置虚拟主机-端口号区分/域名区分

    Nginx实现虚拟机 可以实现在同一台服务运行多个网站,而且网站之间互相不干扰.同一个服务器可能有一个ip,网站需要使用80端口.网站的域名不同. 区分不同的网站有三种方式:ip区分.端口区分.域名区 ...

  8. redis学习 --List

    1:添加   lpush    rpush     如果没有key的话,会报错.   lpushx    rpushx    如果没有key的话,不做任何反应. 2:得到 lpop   rpop   ...

  9. 28 August

    单调队列复习. 投资 (invest) 给定一带符号整数数列,求长度为 \([s, e]\) 的子区间的和的最大值.(最大子段和) 降二维为一维,for循环枚举区间右端点.预处理前缀和,问题转化为找到 ...

  10. laravel5.6 操作数据 Eloquent ORM

    建立Users模型 <?php namespace App\Model\Eloquent\Admin; use Illuminate\Database\Eloquent\Model; class ...