樹形DP+DFS序+樹狀數組 HDOJ 5293 Tree chain problem(樹鏈問題)

 

題目連接php

題意:html

  有n個點的一棵樹。其中樹上有m條已知的鏈,每條鏈有一個權值。從中選出任意個不相交的鏈使得鏈的權值和最大。c++

思路:數組

  樹形DP。設dp[i]表示i的子樹下的最優權值和,sum[i]表示不考慮i點時子樹的最優權值和,即(j是i的兒子),顯然dp[i]>=sum[i]。那麼問題是考慮i點時dp[i]的值是多少,假設有一條鏈經過i,且端點a和b都在i的子樹裏,即LCA(a,b)=i,若是考慮加上這條鏈的權值,那麼a->i, b->i的路上的點v都不能有鏈通過它們(題目要求鏈不相交),那麼-dp[v],但至少有sum[v],即,其中v是某條鏈上的點。那麼怎麼快速求出sigma的值呢,想到樹狀數組維護前綴和。那麼怎麼遍歷呢,用DFS序遍歷,思想和「糧食分配」同樣,在L[v]上修改,在R[v]上恢復。this

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;
const int D = 20;

struct Chain {
    int u, v, w;
};
vector<Chain> chains[N];
vector<int> edges[N];
int dp[N], sum[N];
int n, m;
int tim;

void init() {
    for (int i=1; i<=n; ++i) {
        edges[i].clear ();
        chains[i].clear ();
    }
}

int rt[N][D], dep[N];

void init_LCA() {
    for (int j=1; j<D; ++j) {
        for (int i=1; i<=n; ++i) {
            rt[i][j] = rt[i][j-1] ? rt[rt[i][j-1]][j-1] : 0;
        }
    }
}

int LCA(int u, int v) {
    if (dep[u] < dep[v]) swap (u, v);
    for (int i=0; i<D; ++i) {
        if ((dep[u] - dep[v]) >> i & 1) {
            u = rt[u][i];
        }
    }
    if (u == v) return u;
    for (int i=D-1; i>=0; --i) {
        if (rt[u][i] != rt[v][i]) {
            u = rt[u][i];
            v = rt[v][i];
        }
    }
    return rt[u][0];
}

struct BIT {
    int C[N];
    int n;
    void init(int n) {
        this->n = n;
        memset (C, 0, sizeof (C));
    }
    void updata(int i, int x) {
        for (; i<=n; i+=i&-i) C[i] += x;
    }
    int query(int i) {
        int ret = 0;
        for (; i>0; i-=i&-i) ret += C[i];
        return ret;
    }
}bsum, bdp;

int L[N], R[N];

void DFS(int u, int pa) {
    L[u] = tim++;
    dep[u] = dep[pa] + 1;
    rt[u][0] = pa;
    for (auto v: edges[u]) {
        if (v == pa) continue;
        DFS (v, u);
    }
    R[u] = tim;
}

void DFS(int u) {
    sum[u] = 0;
    for (auto v: edges[u]) {
        if (v == rt[u][0]) continue;
        DFS (v);
        sum[u] += dp[v];
    }
    dp[u] = sum[u];
    for (auto chain: chains[u]) {
        int a = chain.u, b = chain.v, c = chain.w;
        int tmp = bsum.query (L[a]) - bdp.query (L[a]) + bsum.query (L[b]) - bdp.query (L[b]);
        dp[u] = max (dp[u], sum[u] + tmp + c);
    }
    bsum.updata (L[u], sum[u]);
    bsum.updata (R[u], -sum[u]);
    bdp.updata (L[u], dp[u]);
    bdp.updata (R[u], -dp[u]);
}

void prepare() {
    dep[0] = 0;
    tim = 1;
    DFS (1, 0);
    init_LCA ();
    bsum.init (n);
    bdp.init (n);
}

int main() {
    int T;
    scanf ("%d", &T);
    while (T--) {
        init ();
        scanf ("%d%d", &n, &m);
        for (int i=1; i<n; ++i) {
            int u, v;
            scanf ("%d%d", &u, &v);
            edges[u].push_back (v);
            edges[v].push_back (u);
        }
        
        prepare ();

        for (int i=1; i<=m; ++i) {
            int u, v, w;
            scanf ("%d%d%d", &u, &v, &w);
            int lca = LCA (u, v);
            chains[lca].push_back ((Chain) {u, v, w});
        }

        DFS (1);
        printf ("%d\n", dp[1]);
    }
    return 0;
}
相關文章
相關標籤/搜索