数据结构总结

一、树链刨分

按照重儿子分就行了,理论复杂度是log^2的,但事实上常数比较小。

我YY了一个优化的方法:如果题目只涉及路径的修改,可以针对每个重链单独建一棵线段树(这样必须用指针表示儿子),然后可以发现除了u,v,lca(u,v)三个点需要深入线段树中,其他的重链在线段树的根节点读了值就直接返回了,这样写复杂度是logn的,操作量特别大的题可以看出明显的差距。

但是如果题目同时涉及路径和子树的修改(NOI2015D1T2),就必须老老实实按dfs序来建树了。因为重链上的点在dfs序中是连续的,所以dfs序既可以实现子树修改(logn),也可以实现路径修改(log^2n),比较巧妙。

理论上如果点数在20w以内,DFS函数内只有两个参数,在Linux下可以写递归的。如果特别大,好像不能用BFS,要用手工栈的DFS,不然没法采集dfs序。

本来链剖是用来维护点权的,如果要维护边权,把它下放到点上即可。

int htp[MAXN], hsn[MAXN], sz[MAXN];
int fa[MAXN], dep[MAXN], pp[MAXN], fp[MAXN];
void DFS1(int u) //注意最好将fa定义在全局数组,减少DFS的栈空间开销。
{
	sz[u] = 1;
	for (Ed*p = adj[u]; p; p=p->nxt)
		if (p->to!=fa[u]) {
			dep[p->to] = dep[u] + 1;
			fa[p->to] = u;
			DFS1(p->to);
			sz[u] += sz[p->to];
			if (sz[p->to] > sz[hsn[u]]) hsn[u] = p->to;
		}
}
int tmr;
void DFS2(int u, int tp)
{
	htp[u] = tp;
	pp[u] = ++tmr; //即dfn,映射到线段树中的节点
	fp[tmr] = u;   //线段树的节点反馈回原树,实际上并不常用。
	if (hsn[u]) DFS2(hsn[u], tp); //保证重链有的连续的dfs序
	for (Ed *p = adj[u]; p; p=p->nxt)
		if (p->to != fa[u] && p->to != hsn[u])
			DFS2(p->to, p->to);
}

查询和修改,注意题目是边权还是点权,如果是边权的话则不统计lca(u,v),如果是点权则需要。

void paint(int u, int v, int c)
{
	for (int f1=htp[u], f2=htp[v]; f1 != f2; )
	{
		if (dep[f1] < dep[f2]) swap(f1,f2), swap(u, v);
		sg.ins(1, pp[f1], pp[u], c);
		u = fa[f1], f1 = htp[u];
	}
	if (dep[u] > dep[v]) swap(u, v);
	sg.ins(1, pp[u], pp[v], c); //sg为线段树结构体
}



二、比较快的二叉查找树。

你可能感兴趣的:(学习日志)