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)),则(Acup 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)
[egin{bmatrix}
f(u,0) \
f(u,1)
end{bmatrix}
=
egin{bmatrix}
g(u,0) & g(u,0) \
g(u,1) & -infty
end{bmatrix}
egin{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
", max(ans.g[0][0], ans.g[1][0]));
}
return 0;
}