LCT小结

最近写了几题LCT,算是复习和拓展吧,原来写的LCT都是静态的……下一步打算把QTREE系列都给写了吧……


LCT可以维护树上的路径,路径也即一个序列,于是就用Splay来维护这个路径序列。LCT支持查询树上两点之间的路径的各种信息(基本上Splay能存的LCT都行),并且可以改变树的形态,复杂度都是均摊O(logN)的。

关于LCT的介绍网上到处都是,论文都有好几篇……LCT的操作大概有下面几个:expose(也有叫access的,但感觉expose要霸气一些……)、query、modify、link、cut、makeRoot。

expose x的作用是提出从x到根的路径,后面的操作中基本上都会用到expose。

query和modify这是对路径上的权值进行修改,expose之后用Splay操作就行了。

makeRoot x即换根。换根影响的节点只有从x到当前根的路径上的节点,只需expose x再splay x,之后把Splay中的x节点打上翻转标记即可。

link x y即在两点之间连边。先makeRoot x,再把x的父节点指向y即可。

cut x y即删去x和y之间的边。要考虑x是y的父亲和y是x的父亲的两种情况,expose x再splay y,如果此时y的父节点为x则说明x是y的父亲,清除y的父节点指针。不然再反着来一遍。

cut操作之所以要这样是有理由的。如果x是y的父亲那么expose x之后提出的就是x到根的路径,上面一定不含y,而且此时x的左右儿子也都不是y。这时候再splay y就可以直接清除标记了。

另外注意,即使x和y没有直接相连,cut x y还是会使x和y不连通。所以假如题目没有保证给定的x和y一定直接相连,那么要先判断。可以用set维护与每个点相连的点,或者expose y再splay y,然后检查y的近根端的最远点(即y的前驱)是否为x;如果不是就反着来一遍。


题目:

BZOJ1036

给定一棵树,每个点有点权。操作有:1、询问两点之间路径上的最大权;2、询问两点之间路径上的权值和,3、修改点权。

裸的LCT。


BZOJ2002

给定一个序列S1~Sn。Si代表如果到了i号位置,那么下一次就到i+Si号位置,如果i+Si>n则跳出了序列。操作有:1、询问从i号位置出发,要跳几次才能跳出序列;2、修改Si。

对于每个Si如果i+Si<=n那么从i+Si点向i点连边,那么询问即求从某点到其根的距离,修改则是改变树的形态。求到根的距离实际上就是求从该点到根的路径上的点的个数(显然……),答案即expose之后左子树的大小+1。


BZOJ2049

给定N个点,一开始没有边。操作有:1、在x和y之间连边;2、摧毁x和y之间的边(保证之前有在x和y之间连边的操作);3、询问x和y是否在一个连通块内。保证任意时刻N个点构成森林。

连边和删边都是形态变换,询问是否连通则可以类似query做。令expose x返回最后一次虚边变为实边时父节点的编号,那么expose x再expose y返回的就是LCA(前提是x和y连通)。如果沿着这个“LCA”一路向右能走到x的话那么x和y连通,否则不连通。


SPOJ QTREE

给定一棵树,每条边有边权。操作有:1、询问两点之间路径上的最大权;2、修改边权。

随便选一个根,将边权赋给边的两个端点中离根较远的点上,然后就是裸LCT了。


SPOJ QTREE2

给定一棵树,每条边有边权。操作有:1、询问两点之间路径的长度;2、询问一点到另一点路径上的第k个点。

询问2即expose提出的链上的第k个点,用Splay的find操作即可。注意判断第k个点在是在x的支链上还是y的支链上。


BZOJ2594

给定一个N个点M条边的无向图,每条边有边权。操作有:1、询问两点之间所有路径中最大边权最小的路径上的最大边权;2、删去一条边。

这题是WC2006水管局长的数据加强版。首先明确,操作1询问的最大边权的边一定在整个图的最小生成树上(考虑之前的删边操作)。

接下来就是如何处理删边。如果直接处理,似乎好找要加入哪条边。那么我们按倒序离线处理询问,把删边转换为加边。假设加上边(x,y),权值为w,那么找到LCT上x到y的路径上权值最大的边,如果改边的权值>w,那么删去该边,然后link x y。

不过由于这里要改变树的形态,不太好把边权压到点上,所以可以将每条边视为一个点,那么边e(x,y)在LCT中即x->e->y。把点权设为0就不会影响查询操作。


代码:

只发BZOJ2594的,所有的操作这里面都有……感觉这个模板应该不算慢吧,加了FastIO在BZOJ上跑了11s……

//BZOJ2594; 水管局长STRONG_DATA (WC2006); Link-Cut Tree
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define N 100000
#define M 1000000
#define Q 100000
#define SIZE 33000000
using namespace std;

class inedge
{
public:
	int a, b, w, num;
	bool v;
	inedge() : v(false) {}
	inedge(int _a, int _b, int _w) : a(_a), b(_b), w(_w), v(false) {}
	inedge(int _a, int _b, int _w, int _num) : a(_a), b(_b), w(_w), num(_num), v(false) {}
}e[M + 1], qt[Q + 1];
class Node
{
public:
	Node *l, *r, *f, *p;
	int m, v, num;
	bool rev;
	Node() {}
	Node(int _v, int _num) : l(NULL), r(NULL), f(NULL), rev(false), v(_v), m(_v), p(this), num(_num) {}
}*p[N << 1 | 1];
bool op[Q + 1];
int n, m, q, tot, cnt, x, y, w, ans[Q + 1], f[N + 1];
char inbuf[SIZE], *ip = inbuf;

inline int getint()
{
	int r = 0;
	while (*ip < '0' || *ip > '9') ++ip;
	while (*ip >= '0' && *ip <= '9') r = r * 10 + *(ip++) - '0';
	return r;
}

inline bool cmp(const inedge &x, const inedge &y)
{ return x.a == y.a ? x.b < y.b : x.a < y.a; }

inline bool cmp2(const inedge &x, const inedge &y)
{ return x.w < y.w; }

int find(int x)
{ return x == f[x] ? x : f[x] = find(f[x]); }

inline bool isRoot(Node *x)
{ return !(x->f) || (x->f->l != x && x->f->r != x); }

inline void push(Node *x)
{
	if (!(x->rev)) return ;
	x->rev = false;
	if (x->l) x->l->rev ^= 1;
	if (x->r) x->r->rev ^= 1;
	swap(x->l, x->r);
}

inline void update(Node *x)
{
	x->m = x->v, x->p = x;
	if (x->l && x->l->m > x->m) x->m = x->l->m, x->p = x->l->p;
	if (x->r && x->r->m > x->m) x->m = x->r->m, x->p = x->r->p;
}

inline void zig(Node *x)
{
	Node *y = x->f;
	Node *z = y->f;
	push(x);
	if (z && z->l == y) z->l = x;
	else if (z && z->r == y) z->r = x;
	if (x->r) x->r->f = y;
	y->l = x->r;
	x->r = y, y->f = x, x->f = z;
	update(y);
}

inline void zag(Node *x)
{
	Node *y = x->f;
	Node *z = y->f;
	push(x);
	if (z && z->l == y) z->l = x;
	else if (z && z->r == y) z->r = x;
	if (x->l) x->l->f = y;
	y->r = x->l;
	x->l = y, y->f = x, x->f = z;
	update(y);
}

inline void splay(Node *x)
{
	Node *y, *z;
	while (!isRoot(x))
	{
		y = x->f;
		z = y->f;
		if (z) push(z);
		push(y);
		if (isRoot(y))
			if (y->l == x) zig(x);
			else zag(x);
		else
			if (z->l == y)
				if (y->l == x) zig(y), zig(x);
				else zag(x), zig(x);
			else
				if (y->l == x) zig(x), zag(x);
				else zag(y), zag(x);
	}
	push(x);
	update(x);
}

inline void expose(Node *x)
{
	for (Node *y = NULL; x; x = x->f)
	{
		splay(x);
		x->r = y;
		update(y = x);
	}
}

inline void makeRoot(Node *x)
{
	expose(x);
	splay(x);
	x->rev ^= 1;
}

inline void cut(Node *x, Node *y)
{
	expose(y);
	splay(x);
	if (x->f == y)
	{
		x->f = NULL;
		if (y->l == x) y->l = NULL;
		else if (y->r == x) y->r = NULL;
	}
	else
	{
		expose(x);
		splay(y);
		y->f = NULL;
		if (x->l == y) x->l = NULL;
		else if (x->r == y) x->r = NULL;
	}
}

inline void link(Node *x, Node *y)
{
	makeRoot(x);
	x->f = y;
}

inline int findedge(int x, int y)
{ return lower_bound(e + 1, e + m + 1, inedge(x, y, 0), cmp) - e; }

inline Node* query(Node *x, Node *y)
{
	Node *r = NULL;
	expose(y);
	for (y = NULL; x; x = x->f)
	{
		splay(x);
		if (!(x->f))
		{
			if (x->r && (!r || x->r->m > r->m)) r = x->r->p;
			if (y && (!r || y->m > r->m)) r = y->p;
		}
		x->r = y;
		update(y = x);
	}
	return r;
}

inline void modify(inedge &c)
{
	Node *x = p[c.a], *y = p[c.b], *r;
	r = query(x, y);
	if (r->v <= c.w) return ;
	cut(r, p[e[r->num].a]);
	cut(r, p[e[r->num].b]);
	delete r;
	r = new Node(c.w, c.num);
	link(r, x);
	link(r, y);
}

int main()
{
//	freopen("input.txt", "r", stdin);
//	freopen("output.txt", "w", stdout);
	fread(inbuf, sizeof(char), sizeof(char) * SIZE, stdin);
//	scanf("%d%d%d", &n, &m, &q);
	n = getint(), m = getint(), q = getint();
	for (int i = 1; i <= n; ++i) p[i] = new Node(0, -i), f[i] = i;
	for (int i = 1; i <= m; ++i)
	{
//		scanf("%d%d%d", &x, &y, &w);
		x = getint(), y = getint(), w = getint();
		if (x > y) swap(x, y);
		e[i] = inedge(x, y, w);
	}
	sort(e + 1, e + m + 1, cmp);
	for (int i = 1; i <= m; ++i) e[i].num = i;
	for (int i = 1; i <= q; ++i)
	{
//		scanf("%d%d%d", &w, &x, &y);
		w = getint(), x = getint(), y = getint();
		if (x > y) swap(x, y);
		qt[i] = inedge(x, y, 0);
		op[i] = w == 1;
		if (w == 1) continue;
		int pos = findedge(x, y);
		e[pos].v = true, qt[i].num = pos, qt[i].w = e[pos].w;
	}
	tot = m;
	for (int i = 1; i <= tot; ++i)
		if (e[i].v) swap(e[i--], e[tot--]);
	sort(e + 1, e + tot + 1, cmp2);
	for (int i = 1; i <= tot; ++i) e[i].v = false;
	cnt = n;
	for (int i = 1; i <= tot; ++i)
		if (find(e[i].a) != find(e[i].b))
		{
			p[++cnt] = new Node(e[i].w, e[i].num);
			link(p[e[i].a], p[cnt]);
			link(p[cnt], p[e[i].b]);
			f[find(e[i].a)] = find(e[i].b);
			e[i].v = true;
		}
	sort(e + 1, e + m + 1, cmp);
	
	for (int i = q; i > 0; --i)
		if (op[i])
			ans[i] = query(p[qt[i].a], p[qt[i].b])->v;
		else modify(qt[i]);
	
	for (int i = 1; i <= q; ++i)
		if (op[i])
			printf("%d\n", ans[i]);
	
	return 0;
}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值