「学习笔记」树相关算法

DSU on tree

保留重儿子答案,轻儿子暴力求。

线段树树上合并

\(O(n \log n)\)。考虑每次合并复杂度是O(删的点个数),点数是\(O(n \log n)\)的。

dfs序系列

2-dfs序:dfs进出的时候给一个点,给一个+1和-1系数。可以表示根到点的链信息。

欧拉序:\(O(1)\) lca

树的直径

性质:

1 若点集\(A,B\),直径为\((a,b),(c,d)\),则\(A\cup B\)的直径为\((a,c),(a,d),(b,c),(b,d)\)中的一条

2 点集\(S\)中,离点\(u\)的最远点一定是某条直径某个端点

动态dp

经典问题:树上带修改点权的最大独立集

考虑定义一种广义矩阵乘法:\(add-max\)。即\((A*B)_{i,j}=max(A_{i,k}+B_{k,j})。\)可以证明具有结合律。

我们还可能构造出单位矩阵:\(e_{i,j}=[i!=j] \infty\)

定义\(f(u,0/1)\)为u不选和选的最大权,\(g(u, 0/1)\)为u选和不选,不包含重子树的最大值

重链剖分,用线段树维护\(g\)

这样:(记重儿子为son)

\[ \begin{bmatrix} f(u,0) \\ f(u,1) \end{bmatrix} = \begin{bmatrix} g(u,0) & g(u,0) \\ g(u,1) & -\infty \end{bmatrix} \begin{bmatrix} f(son_u,0) \\ f(son_u,1) \\ \end{bmatrix} \]

每次修改点权,把自己的\(g\)改一下,然后把到根路径上的轻链的fa的g改一下

答案就是1所在重链的g的乘积。

洛谷P4719 【模板】动态 DP:

#include 
#include 
#include 
#include 
using namespace std;

const int N = 1e5 + 10;
const int INF = 1e9 + 10;

struct mat {
    int g[2][2];
    mat() { memset(g, 0, sizeof g); }
    void set() { g[0][0] = g[1][1] = 0; g[0][1] = g[1][0] = - INF; }
    mat operator * (const mat &b) {
        mat ans;
        for(int i = 0; i < 2; i ++) {
            for(int j = 0; j < 2; j ++) {
                for(int k = 0; k < 2; k ++) {
                    ans.g[i][j] = max(ans.g[i][j], g[i][k] + b.g[k][j]);
                }
            }
        }
        return ans;
    }
} t[N << 2], g[N], ans;
int n, q, f[N][2], val[N], sz[N], son[N], fa[N];
int idx, dfn[N], top[N], pos[N], edp[N];
vector G[N];
void dfs(int u, int p = 0) {
    sz[u] = 1; fa[u] = p;
    for(int i = 0; i < G[u].size(); i ++) {
        int v = G[u][i];
        if(v == p) continue ;
        dfs(v, u); sz[u] += sz[v];
        if(sz[v] > sz[son[u]]) son[u] = v;
    }
    g[u].g[1][0] = f[u][1] = val[u];
    for(int i = 0; i < G[u].size(); i ++) {
        int v = G[u][i];
        if(v == p) continue ;
        f[u][0] += max(f[v][1], f[v][0]);
        f[u][1] += f[v][0];
        if(v == son[u]) continue ;
        g[u].g[1][0] += f[v][0];
        g[u].g[0][0] += max(f[v][0], f[v][1]);
    }
    g[u].g[0][1] = g[u].g[0][0];
    g[u].g[1][1] = - INF;
}
void dfs2(int u, int t) {
    top[u] = t; dfn[u] = ++ idx; pos[idx] = u; edp[t] = u;
    if(!son[u]) return ;
    dfs2(son[u], t);
    for(int i = 0; i < G[u].size(); i ++) {
        int v = G[u][i];
        if(v != fa[u] && v != son[u]) {
            dfs2(v, v);
        }
    }
}
void pu(int u) { t[u] = t[u << 1] * t[u << 1 | 1]; }
void build(int u, int l, int r) {
    if(l == r) { t[u] = g[pos[l]]; return ; }
    int mid = (l + r) >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
    pu(u);
}
void modify(int u, int l, int r, int p) {
    if(l == r) { t[u] = g[pos[l]]; return ; }
    int mid = (l + r) >> 1;
    if(p <= mid) modify(u << 1, l, mid, p);
    else modify(u << 1 | 1, mid + 1, r, p);
    pu(u);
}
void qry(int u, int l, int r, int ql, int qr) {
    if(l == ql && r == qr) { ans = ans * t[u]; return ; }
    int mid = (l + r) >> 1;
    if(qr <= mid) qry(u << 1, l, mid, ql, qr);
    else if(ql > mid) qry(u << 1 | 1, mid + 1, r, ql, qr);
    else {
        qry(u << 1, l, mid, ql, mid);
        qry(u << 1 | 1, mid + 1, r, mid + 1, qr);
    }
}
void change(int u, int w) {
    g[u].g[1][0] += w - val[u]; val[u] = w;
    while(u) {
        ans.set(); qry(1, 1, n, dfn[top[u]], dfn[edp[top[u]]]);
        mat la = ans;
        modify(1, 1, n, dfn[u]);
        ans.set(); qry(1, 1, n, dfn[top[u]], dfn[edp[top[u]]]);
        mat now = ans;
        u = fa[top[u]];
        g[u].g[0][0] += max(now.g[0][0], now.g[1][0]) - max(la.g[0][0], la.g[1][0]);
        g[u].g[0][1] = g[u].g[0][0];
        g[u].g[1][0] += now.g[0][0] - la.g[0][0];
    }
}
int main() {
    scanf("%d%d", &n, &q);
    for(int i = 1; i <= n; i ++) {
        scanf("%d", val + i);
    }
    for(int i = 1, u, v; i < n; i ++) {
        scanf("%d%d", &u, &v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    dfs(1); dfs2(1, 1); build(1, 1, n);
    for(int i = 0, u, w; i < q; i ++) {
        scanf("%d%d", &u, &w); change(u, w);
        ans.set(); qry(1, 1, n, 1, dfn[edp[1]]);
        printf("%d\n", max(ans.g[0][0], ans.g[1][0]));
    }
    return 0;
}

你可能感兴趣的:(「学习笔记」树相关算法)