「学习笔记」树相关算法
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:g(u,0)=\sum_{v!=son}\max(f[v][0], f[v][1]), g(u,1)=val[u] + \sum_{v!=son}f[v][0]\)
重链剖分,用线段树维护\(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改一下。更新的时候有个小trick,把之前的减去,把新的加上。不然重算复杂度是错的。
答案就是1所在重链的g的乘积。
洛谷P4719 【模板】动态 DP:
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
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<int> 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;
}