uva 11994(LCT)

题目链接:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=24&page=show_problem&problem=3145

给出一个森林, 要求支持三种操作:

    1.将节点x和它的父节点p[x]的边砍断, 并使x成为y的一个子节点, 若x为y的祖先则忽略, 

            并将x,y之间的边的颜色设为c。

    2.将x到y路径上的边的颜色置为c

    3.查询x到y路径上的边数和边上不同的颜色数。

刘汝佳数据结构专场的题,看到第一种操作不难想到是LCT,由于只有30种颜色我们可以考虑状压, 在push_up, push_down里面维护一下就差不多是模板题了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include <cmath>

using namespace std;

const int N = 50005;

typedef unsigned int UINT32;

const UINT32 m1  = 0x55555555;  // 01010101010101010101010101010101
const UINT32 m2  = 0x33333333;  // 00110011001100110011001100110011
const UINT32 m4  = 0x0f0f0f0f;  // 00001111000011110000111100001111
const UINT32 m8  = 0x00ff00ff;  // 00000000111111110000000011111111
const UINT32 m16 = 0x0000ffff;  // 00000000000000001111111111111111
const UINT32 h01 = 0x01010101;  // the sum of 256 to the power of 0, 1, 2, 3

inline int count_1(UINT32 x) {
	x = (x & m1) + ((x >> 1) & m1);
	x = (x & m2) + ((x >> 2) & m2);
	x = (x & m4) + ((x >> 4) & m4);
	x = (x & m8) + ((x >> 8) & m8);
	x = (x & m16) + ((x >> 16) & m16);
	return x;
}

struct LinkCutTree {
	int p[N], val[N], l[N], r[N], sz[N], setv[N];
	int col[N];
	bool rev[N], rt[N];
	int n, root;

#define lu l[u]
#define ru r[u]
#define pu p[u]
#define pv p[v]

	void init(int n) {
		this->n = n;
		for (int i = 1; i <= n; i++) {
			rt[i] = 1;
			l[i] = 0, r[i] = 0, rev[i] = 0, col[i] = 0, sz[i] = 1, setv[i] = 0;
		}
	}

	inline void update(int u, int c) {
		if (u == 0) return;
		val[u] = col[u] = setv[u] = 0;
		val[u] = col[u] = setv[u] = c;
	}

	inline void Set(int* a, int u, int v) {
		a[u] = v, pv = u;
	}

	inline void push_up(int u) {
		col[u] = col[lu] | col[ru] | val[u];
		sz[u] = sz[lu] + sz[ru] + 1;
	}

	inline void push_down(int u) {
		if (u == 0) return;
		
		if (rev[u]) {
			swap(lu, ru);
			rev[lu] ^= 1, rev[ru] ^= 1;
			rev[u] = 0;
		}

		if (setv[u]) {
			update(lu, setv[u]), update(ru, setv[u]);
			setv[u] = 0;		
		}
	}

	inline void rotate(int u) {
		int v = pu;
		if (rt[v])
			pu = pv;
		else {
			push_down(pv);
			Set(pu == l[pv] ? l : r, pv, u);
		}

		push_down(v), push_down(u);

		if (u == l[v]) {
			Set(l, v, ru);
			Set(r, u, v);
		}
		else {
			Set(r, v, lu);
			Set(l, u, v);
		}

		if (rt[v]) rt[u] = 1, rt[v] = 0;
		push_up(v);

	}

	void Splay(int u) {
		while (!rt[u]) {
			rotate(u);
		}
	}

	int Access(int u) {
		int v = 0;
		do {
			Splay(u), push_down(u);
			rt[ru] = 1, rt[ru = v] = 0, push_up(u);
			u = p[v = u];
		} while (u);
		return v;
	}

	void Evert(int u) {
		rev[Access(u)] ^= 1;
	}

	int Root(int u) {
		for (u = Access(u); push_down(u), lu; u = lu);
		return u;
	}

	void Link(int u, int v) {
		Evert(u), Splay(u), p[u] = v, Access(u);	
	}

	int Cut(int v, int u) {//v is current root
		Evert(v), Splay(v), Access(u), Splay(u);
		for (v = lu; r[v]; v = r[v]);
		p[lu] = p[u], rt[lu] = 1, p[u] = lu = 0;
		return v;
	}

	void op1(int u, int v, int c) {
		if (u == v) return;
		int fu = Root(u), pa = 0;
		if (fu != u)
			pa = Cut(fu, u);
		if (u == Root(v)) {
			if (pa)
				Link(u, pa);
		}
		else {
			Link(u, v);
			Splay(u);
			val[u] = 1 << (c - 1);
			push_up(u);				
		}
	}

	void op2(int u, int v, int c) {
		if (Root(u) != Root(v) || u == v)
			return;
		else {
			Access(v), v = 0;
			do {
				Splay(u), push_down(u);
				if (!p[u]) update(ru, (1 << c - 1)), update(v, (1 << c - 1));
				rt[ru] = 1, rt[ru = v] = 0, push_up(u);		
				u = p[v = u];
			} while (u);
		}
	}

	void op3(int u, int v) {
		if (u == v || Root(u) != Root(v)) {
			puts("0 0");
		}
		else {
			Access(v), v = 0;
			do {
				Splay(u), push_down(u);
				if (!p[u]) printf("%d %d\n",  sz[ru] + sz[v], count_1(col[ru] | col[v]));
				rt[ru] = 1, rt[ru = v] = 0, push_up(u);
				u = p[v = u];
			} while (u);	
		}	
	}

}T;

int main() {
	int n, m, c, u, v, op;
	while (~scanf("%d %d", &n, &m)) {
		T.init(n);
		for (int i = 1; i <= n; i++) {
			scanf("%d", &T.p[i]);
		}

		for (int i = 1; i <= n; i++) {
			scanf("%d", &c);
			if (!T.p[i]) continue;
			T.val[i] = T.col[i] = (1 << c - 1);	
		}

		for (int i = 0; i < m; i++) {
			scanf("%d%d%d", &op, &u, &v);

			if (op == 1) {
				scanf("%d", &c);
				T.op1(u, v, c);
			}
			else if (op == 3)
				T.op3(u, v);
			else if (op == 2) {
				scanf("%d", &c);
				T.op2(u, v, c);
			}
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值