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]);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章