zoukankan      html  css  js  c++  java
  • 「学习笔记」树相关算法

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

    LCT

  • 相关阅读:
    禁止页面全选复制IE,Opera,Mozilla Firefox,Google Chrome,Safari,Flock等主流浏览器测试成功
    Create virtual keyboard using C# Winform Application
    C# 获取Windows语言类型(两种方式)
    Javascript在ASP.NET中的用法:计算还剩余输入多少个字符
    IE和firefox通用的复制到剪贴板的JS函数,Opera测试不成功!
    什么是数据的表分区(文章附上Server 2005分区实施方案)
    JavaScript创建的可编辑表格
    关于ASP.NET页面打印技术的总结
    错误:该行已经属于另一个表
    安装IE7后测试IE6环境的解决办法_IE6Standalone
  • 原文地址:https://www.cnblogs.com/hongzy/p/11617959.html
Copyright © 2011-2022 走看看