「学习笔记」树相关算法

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;
}

LCT

posted @ 2019-10-02 18:26  hfhongzy  阅读(455)  评论(5编辑  收藏  举报