hdu5293 Tree chain problem 树形dp+线段树

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5293

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

比赛的时候以为是树链剖分就果断没去想,事实上是没思路。

看了题解,原来是树形dp。话说多校第一场树形dp还真多。

。。

维护d[i],表示以i为根节点的子树的最优答案。

sum[i]表示i的儿子节点(仅仅能是儿子节点)的d值和。

那么答案就是d[root]。

怎样更新d值

d[i] = max(sum[i] , w[p]+sigma sum[j] -
sigma d[j] );   p为以i为lca的链 。j为链上的点(注意:不须要减去d[i])

也就是。对于i,

要么是不选i,答案即sum[i]。他的儿子和。

要么是选i,即找一条过i的链,链的权值 + 这棵子树上不在链上的距离链近期的点的d值和。

维护链上的的sum和,d和 用线段树。

先通过树的遍历序将树上的节点链话。然后区间改动,单点查询。(每一个点的信息是改点到根节点路径上的和)

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<cmath>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;
const int N = 100000+10;
typedef long long ll;
const int maxh = 25;
int st[N],ed[N],clock;

// ************************************
#define Lson Ls,L,mid
#define Rson Rs,mid+1,R

const int maxn = 100000+10;
const int inf = 100000000;
struct Node{
    ll val, set;
    ll val2 , set2;
}tree[N<<2];
inline void update(Node& fa, Node& Ls, Node& Rs){
    fa.val = Ls.val + Rs.val;
    fa.val2 = Ls.val2 + Rs.val2;
}
inline void pushdown(Node& fa, Node& Ls, Node& Rs){
    if (fa.set != 0){
        Ls.val += fa.set; Ls.set += fa.set;
        Rs.val += fa.set; Rs.set += fa.set;
        fa.set = 0;
    }
    if (fa.set2 != 0){
        Ls.val2 += fa.set2; Ls.set2 += fa.set2;
        Rs.val2 += fa.set2; Rs.set2 += fa.set2;
        fa.set2 = 0;
    }

}
void insert(int v, int L, int R, int p, int q, ll delta,ll delta2){
    if(p<=st[2]&& q>=st[2]&& delta2) {
    }
    if (p<=L && R<=q){
        tree[v].val += delta;
        tree[v].set += delta;

        tree[v].val2 += delta2;
        tree[v].set2 += delta2;
        return;
    }
    int Ls = v<<1, Rs = Ls+1, mid = (L+R)/2;
    pushdown(tree[v], tree[Ls], tree[Rs]);
    if (q<=mid) insert(Lson, p, q, delta,delta2);
    else if (p>mid) insert(Rson, p, q, delta,delta2);
    else{
        insert(Lson, p, q, delta,delta2);
        insert(Rson, p, q, delta,delta2);
    }
//    update(tree[v], tree[Ls], tree[Rs]);
}
ll query(int v, int L, int R,int ql, int qr){
    if(ql<=L && R<=qr){
        return tree[v].val;
    }
    int Ls = v<<1, Rs = Ls+1, mid = (L+R)/2;
    pushdown(tree[v], tree[Ls], tree[Rs]);
    ll ans = 0;
    if(qr<=mid) ans = query(Lson,ql,qr);
    else if(ql>mid) ans = query(Rson,ql,qr);
    else{
        ans = query(Lson,ql,qr) + query(Rson,ql,qr);
    }
//    update(tree[v], tree[Ls], tree[Rs]);
    return ans ;
}
ll query2(int v, int L, int R,int ql, int qr){
    if(ql<=L && R<=qr){
        return tree[v].val2;
    }
    int Ls = v<<1, Rs = Ls+1, mid = (L+R)/2;
    pushdown(tree[v], tree[Ls], tree[Rs]);
    ll ans = 0;
    if(qr<=mid) ans = query2(Lson,ql,qr);
    else if(ql>mid) ans = query2(Rson,ql,qr);
    else{
        ans = query2(Lson,ql,qr) + query2(Rson,ql,qr);
    }
//    update(tree[v], tree[Ls], tree[Rs]);
    return ans ;
}

// ***************************************

ll d[N],sum[N];
vector<int> g[N];
vector<int> list[N];
struct pp{
    int u,v,w;
}p[N];
int n,m,dep[N];
int anc[N][maxh+1];

void dfs(int x,int pre){
    st[x] = ++clock;
    anc[x][0] = pre;
    for(int i=0;i<g[x].size();i++)if(g[x][i]!=pre){
        int u = g[x][i];
        dep[u] = dep[x] + 1;
        dfs(u,x);
    }
    ed[x] = clock;
}
void lca_init(){
    clock = 0;
    dep[1] = 1;
    dfs(1,-1);
    for(int i=1;i<=maxh-1;i++){
        for(int j=1;j<=n;j++)
            if(anc[j][i-1]<0) anc[j][i] = -1;
            else anc[j][i] = anc[anc[j][i-1]][i-1];
    }
}
int swim(int x,int H){
    for(int i=0;H;i++){
        if(H&1) x = anc[x][i];
        H >>= 1;
    }
    return x;
}
int lca(int u,int v){
    if(dep[u]>dep[v]) swap(u,v);
    int H = dep[v]-dep[u];
    v = swim(v,H);
    if(u==v) return u;
    for(int k=maxh-1;k>=0;k--)
        if(anc[u][k]!=anc[v][k]){
            u = anc[u][k];
            v = anc[v][k];
        }
    return anc[u][0];
}

ll find(int u,int v,int la){
    ll ans = 0;
    ll s1 = query2(1,1,n,st[u],st[u]);
    ll s2 = query2(1,1,n,st[v],st[v]);
    ans = s1 + s2;

    ll d1 = query(1,1,n,st[u],st[u]);
    ll d2 = query(1,1,n,st[v],st[v]);
    ans -= d1 + d2;

    return ans+sum[la];
}

ll dfs_tree(int x,int pre){
    if(d[x]!=-1) return d[x];
    ll res = 0;
    sum[x] = 0;
    for(int i=0;i<g[x].size();i++)if(g[x][i]!=pre){
        int u = g[x][i];
        sum[x] += dfs_tree(u,x);
    }
    d[x] = sum[x];

    for(int i=0;i<list[x].size();i++){
        int id = list[x][i];
        ll tmp = p[id].w ;
        ll tmp1 = find(p[id].u,p[id].v,x);
        tmp += tmp1;
        d[x] = max(d[x] , tmp);
    }

    // update

    insert(1,1,n,st[x],ed[x],d[x],sum[x]);
    return d[x];
}

void solve(){
    memset(d,-1,sizeof(d));
    memset(tree,0,sizeof(tree));
    lca_init();
    for(int i=1;i<=n;i++) list[i].clear();
    for(int i=1;i<=m;i++){
        int u = p[i].u , v = p[i].v;
        int la = lca(u,v);
        list[la].push_back(i);
    }
    dfs_tree(1,-1);
}

int main(){
    int T;
    cin >> T;
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) g[i].clear();
        for(int i=1;i<=n-1;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        for(int i=1;i<=m;i++){
            int u,v,w;
            scanf("%d%d%d",&p[i].u,&p[i].v,&p[i].w);
        }
        solve();
        printf("%I64d\n",d[1]);
    }
    return 0;
}
时间: 2024-10-07 11:28:04

hdu5293 Tree chain problem 树形dp+线段树的相关文章

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

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5293 题意: 给你一些链,每条链都有自己的价值,求不相交不重合的链能够组成的最大价值. 题解: 树形dp, 对于每条链u,v,w,我们只在lca(u,v)的顶点上处理它 让dp[i]表示以i为根的指数的最大值,sum[i]表示dp[vi]的和(vi为i的儿子们) 则i点有两种决策,一种是不选以i为lca的链,则dp[i]=sum[i]. 另一种是选一条以i为lca的链,那么有转移方程:dp[i]=

[2015hdu多校联赛补题]hdu5293 Tree chain problem

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5293 题意:给你一棵n个点的树,和一些该树上的树链,每个树链有一定的权值,要求选出一些不相交的树链,使他们的权值和尽量大 解: 树形dp,dp[x]表示以x为根节点的子树的解,那么就可以基于在当前结点对树链的选和不选进行dp 现在有两个问题: 1.在树链上的哪个点进行选择? 2.选和不选分别如何操作? 对于第一个问题,粗略想一下(在解决问题二的时候会发现),树链上最浅的一个点(也就是树链两端的lca

Codeforces 671D. Roads in Yusland(树形DP+线段树)

调了半天居然还能是线段树写错了,药丸 这题大概是类似一个树形DP的东西.设$dp[i]$为修完i这棵子树的最小代价,假设当前点为$x$,但是转移的时候我们不知道子节点到底有没有一条越过$x$的路.如果我们枚举每条路去转移,会发现这条路沿线上的其他子树的答案难以统计,那怎么办呢,我们可以让这条路向上回溯的时候顺便记录一下,于是有$val[i]$表示必修i这条路,并且修完当前子树的最小代价. 则有转移$dp[x]=min(val[j])$,且$j$这条路必须覆盖$x$. $val[i]=(\sum

poj3162(树形dp+线段树求最大最小值)

题目链接:https://vjudge.net/problem/POJ-3162 题意:给一棵树,求每个结点的树上最远距离,记为a[i],然后求最大区间[l,r]满足区间内的max(a[i])-min(a[i])<=M. 思路:第一步向hdoj2196那题一样树形dp求出每个结点的最长距离,我的另一篇博客中有写到https://www.cnblogs.com/FrankChen831X/p/11375572.html.求出最远距离a[i]后,建立线段树维护区间的最大最小值.然后用两个指针i,j遍

POJ 3162 Walking Race 树形DP+线段树

给出一棵树,编号为1~n,给出数m 漂亮mm连续n天锻炼身体,每天会以节点i为起点,走到离i最远距离的节点 走了n天之后,mm想到知道自己这n天的锻炼效果 于是mm把这n天每一天走的距离记录在一起,成为一段长度为n的数列 现在mm想要从这数列中选出一个连续的区间,要求这个区间的max-min<=m 输出最长的区间 做了一个下午 思路: 分成2个部分: 1.求出数列,即对于一棵树,求出每一个节点能到达的最远距离 2.对于这段数列,选出一个区间,使得区间的max-min<=m,并且使得区间长度尽量

Codeforces Round #530 (Div. 2)F Cookies (树形dp+线段树)

题:https://codeforces.com/contest/1099/problem/F 题意:给定一个树,每个节点有俩个信息x和t,分别表示这个节点上的饼干个数和先手吃掉这个节点上一个饼干的的时间.然后有先手和后手俩个人. ?先手可以这么操作:在规定总时间T到达某个节点然后一定要返回根节点1,期间可以选择吃掉某些节点上的某些饼干(前提是保证剩下的时间能够回到根节点): ?后手可以这么操作:在先手到达的位置和这个位置的孩子之间的连边选择一条让先手吃得更多的边摧毁掉,也可以跳过这个过程: 问

Codeforces Round #530 (Div. 2) E (树形dp+线段树)

链接: 题意: 给你一棵树,树上有n个节点,每个节点上有ai块饼干,在这个节点上的每块饼干需要花费bi时间,有两个玩家,玩家一可以移动到当前点的子节点也可以申请游戏结束返回根节点并吃沿途的饼干,玩家二可以删除当前点到儿子节点的一条边,走路和吃饼干都消耗时间,会给出一个总时间,在总时间内尽可能的多吃饼干,问最多能吃多少个? 思路: 由于是玩家一先手,那么最开始的最大边则不会被删除,但之后路途的最大边都会被玩家二删除,所以我们对于当前点我们需要求: 1.如果现在回头那么最多可以吃到多少饼干 2.向下

hdu 5293 Tree chain problem(树链剖分+树形dp)

题目链接:hdu 5293 Tree chain problem 维护dp[u], sum[u],dp[u]表示以u为根节点的子树的最优值.sum[u]表示以u节点的所有子节点的dp[v]之和.对于边a,b,w,在LCA(a,b)节点的时候进行考虑.dp[u] = min{dp[u], Sum(a,b) - Dp(a,b) + sum[u] | (ab链上的点,不包括u } #pragma comment(linker, "/STACK:1024000000,1024000000")

hdu5293(2015多校1)--Tree chain problem(树状dp)

Tree chain problem Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 262    Accepted Submission(s): 59 Problem Description Coco has a tree, whose vertices are conveniently labeled by 1,2,-,n. The