HDU 5293 Tree chain problem - 树形dp&树状数组优化

题目描述

题目大意:

给定一棵有 n 个点的树,以及 m 条树链,其中第 i 条树链 的价值为 wi,请选择一些没有公共点的树链,使得价值和最大。
1 ≤n,m≤ 100000。
Source:2015 Multi-University Training Contest 1

分析:

  • 设f[u]为u为根的子树上选择没有公共点的树链所得的最大价值和。
  • 转移方程:
    枚举链的两端点的lca为u的链,记w为当前链的价值
    f[u]=max{f[u.son],w+kf[k]}
  • 时间复杂度O((n+m)*n),过不了,选择优化,记sum[u]为u的所有儿子v的f[v]之和,得到:

    f[u]=max{f[u.son],w+jsum[j]f[j]}

    (链上的点会在它的父亲的sum[]中多加,要再减去)

    可根据dfs序维护树状数组来实现优化。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define MAXN 100000
#define MAXM 100000
#define MAXLOG 20
typedef long long LL;

struct node{
    int v;
    node *next;
}edge[MAXN*2+10],*adj[MAXN+10],*ecnt=&edge[0];

vector<int> pnt[MAXN+10];
int n,m,LogN,chain[MAXM+10][3];
int fa[MAXN+10],dep[MAXN+10],P[MAXN+10][MAXLOG+10];
int in[MAXN+10],out[MAXN+10],cntd;
LL c[2*MAXN+10],f[MAXN+10];

void Init()
{
    memset(adj,0,sizeof adj);
    ecnt=&edge[0];
    memset(pnt,0,sizeof pnt);
    memset(fa,0,sizeof fa);
    memset(dep,0,sizeof dep);
    memset(f,0,sizeof f);
    cntd=0;
    memset(c,0,sizeof c);
}
void addedge(int u,int v)
{
    node *p=++ecnt;
    p->v=v;
    p->next=adj[u];
    adj[u]=p;
}
void dfs(int u,int prev,int L)
{
    fa[u]=prev;
    dep[u]=L;
    for(node *p=adj[u];p;p=p->next){
        if(p->v==fa[u]) continue;
        dfs(p->v,u,L+1);
    }
}
void LCA_pre()
{
    memset(P,-1,sizeof P);
    for(int i=1;i<=n;i++)
        P[i][0]=fa[i];
    for(LogN=0;(1<<LogN)<=n;LogN++);
    LogN--;
    for(int j=1;j<=LogN;j++){
        for(int i=1;i<=n;i++)
            if(P[i][j-1]!=-1)
                P[i][j]=P[P[i][j-1]][j-1];
    }
}
int LCA(int x,int y)
{
    if(dep[x]<dep[y])
        swap(x,y);
    int Logx;
    for(Logx=0;(1<<Logx)<=dep[x];Logx++);
    Logx--;
    for(int i=Logx;i>=0;i--)
        if(dep[P[x][i]]>=dep[y])
            x=P[x][i];
    if(x==y) return x;
    for(int i=Logx;i>=0;i--)
        if(P[x][i]!=P[y][i])
            x=P[x][i],y=P[y][i];
    return fa[x];
}
void read()
{
    int x,y;
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++){
        scanf("%d%d",&x,&y);
        addedge(x,y);
        addedge(y,x);
    }
    dfs(1,0,1);
    LCA_pre();
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&chain[i][0],&chain[i][1],&chain[i][2]);
        int lca=LCA(chain[i][0],chain[i][1]);
        pnt[lca].push_back(i);
    }
}
int lowbit(int x){
    return x&(-x);
}
void Update(int x,LL d){
    while(x<=2*n){
        c[x]+=d;
        x+=lowbit(x);
    }
}
LL Getsum(int x){
    LL ret=0;
    while(x){
        ret+=c[x];
        x-=lowbit(x);
    }
    return ret;
}
void DP(int u) //dfs for dp on the tree
{
    LL g_u=0;
    in[u]=++cntd;
    for(node *p=adj[u];p;p=p->next){
        if(p->v==fa[u]) continue;
        DP(p->v);
        g_u+=f[p->v];
    }
    out[u]=++cntd;
    Update(in[u],g_u);
    Update(out[u]+1,-g_u);
    f[u]=g_u;
    int side=pnt[u].size();
    for(int i=0,num;i<side;i++){
        num=pnt[u][i];
        f[u]=max(f[u],Getsum(out[chain[num][0]])+Getsum(out[chain[num][1]])+1LL*chain[num][2]-g_u);
    }
    Update(in[u],-f[u]);
    Update(out[u]+1,f[u]);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        Init();
        read();
        DP(1);
        printf("%I64d\n",f[1]);
    }
}

转载于:https://www.cnblogs.com/katarinayuan/p/6572815.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值