SPOJQTREE4 Query on a tree IV

本文介绍了一个关于树结构的问题,其中涉及节点颜色变化和求解白色节点间的最大距离。文章阐述了边分治的思想,通过找到树的中心边来分治处理,并利用优先队列(堆)维护白色节点到根的最长距离。当节点颜色改变时,需要更新距离信息。最后,文章提到了在处理大规模数据时的空间和时间复杂度问题。
摘要由CSDN通过智能技术生成

You are given a tree (an acyclic undirected connected graph) with N nodes, and nodes numbered 1,2,3…,N. Each edge has an integer value assigned to it(note that the value can be negative). Each node has a color, white or black. We define dist(a, b) as the sum of the value of the edges on the path from node a to node b.

All the nodes are white initially.

We will ask you to perform some instructions of the following form:

C a : change the color of node a.(from black to white or from white to black)
A : ask for the maximum dist(a, b), both of node a and node b must be white(a can be equal to b). Obviously, as long as there is a white node, the result will always be non negative.

Input

In the first line there is an integer N (N <= 100000)
In the next N-1 lines, the i-th line describes the i-th edge: a line with three integers a b c denotes an edge between a, b of value c (-1000 <= c <= 1000)
In the next line, there is an integer Q denotes the number of instructions (Q <= 100000)
In the next Q lines, each line contains an instruction “C a” or “A”

Output

For each “A” operation, write one integer representing its result. If there is no white node in the tree, you should write “They have disappeared.”.

Example

Input:

3
1 2 1
1 3 1
7
A
C 1
A
C 2
A
C 3
A

Output:

2
2
0
They have disappeared.

边分治、堆 - AC

先考虑不更改的。
边分治处理与树的路径有关的问题。找到一条中心边,删掉它之后树分成两半,使两半的大小尽可能均匀,达到分治降低复杂度的目的。路径分为经过中心边和不经过的,前者是我们要考虑的;对于后者,递归分治处理。
与中心边相连的两个节点p1,p2分别作为两棵子树的根。经过中心边的最长路径,就是左边子树中白色节点到p1的最长距离,加上右边的,加上中心边。
本来是整个树。分成两半。对于分出来的每个子树,它的答案是由它再分出来的两个子树中的数据处理得到。然后再往上传,传到最大的那个子树,也就是整棵树。这很像是线段树。同样地,边分治处理的这些“子树”要开4倍空间。
不过,有时找不到想要的中心边。比如在菊花图上。所以要加虚点,将多叉树变成二叉树,虚边权为0。不能使用孩子兄弟表示法,那会改变两点间距离。
在这里插入图片描述
据上文,要维护“子树中白色节点到根的最长距离”。节点颜色会变。黑变白,考虑新的白色节点对这个最长距离的贡献;白变黑,删除它的贡献。为此要记录所有节点到根的距离,将白色的加入到堆中,颜色作为删除懒标记。

时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn).

M A X N = 1 0 5 MAXN=10^5 MAXN=105,但我得把它开到 1 0 6 10^6 106才能不RE,不知道哪里写错了。

struct DNode {
	int u, d;
	DNode(int _u, int _d): u(_u), d(_d){}
	bool operator < (const DNode &x) const {
		return x.d > d;
	}
};
struct Node {
	int rt, w, ans, lc, rc;
	priority_queue<DNode> q;
	Node(): lc(-1), rc(-1){}
} t[MAXN << 3];
int m;

int n, n1, siz[MAXN << 1], tot, mx, mide, q;
int head[MAXN << 1], to[MAXN << 2], len[MAXN << 2], nxt[MAXN << 2], js;
int head1[MAXN << 1], to1[MAXN << 2], len1[MAXN << 2], nxt1[MAXN << 2], js1;
bool mark[MAXN << 1], vis[MAXN << 2];
char op[3];

void add(int u, int v, int w) {
	to[js] = v; len[js] = w; nxt[js] = head[u]; head[u] = js++;
}
void add1(int u, int v, int w) {
	to1[js1] = v; len1[js1] = w; nxt1[js1] = head1[u]; head1[u] = js1++;
}

void rebuild(int u, int par) {
	int fa = 0;
	for (int i = head[u]; ~i; i = nxt[i]) {
		int v = to[i], w = len[i];
		if (v == par) continue;
		if (!fa) {
			add1(u, v, w); add1(v, u, w);
			fa = u;
		}
		else {
			mark[++n1] = 1;
			add1(fa, n1, 0); add1(n1, fa, 0);
			fa = n1;
			add1(fa, v, w); add1(v, fa, w);
		}
		rebuild(v, u);
	}
}
void pushup(int i) {
	t[i].ans = -1;
	while (!t[i].q.empty() && mark[t[i].q.top().u]) t[i].q.pop();
	int lc = t[i].lc, rc = t[i].rc;
	if (lc == -1 && rc == -1) {
		if (!mark[t[i].rt]) t[i].ans = 0;
	}
	else {
		ckmax(t[i].ans, max(t[lc].ans, t[rc].ans));
		if (!t[lc].q.empty() && !t[rc].q.empty()) {
			ckmax(t[i].ans, t[lc].q.top().d + t[rc].q.top().d + t[i].w);
		}
	}
}
void setmide(int u, int x, int i, int dep) {
	siz[u] = 1;
	add(u, i, dep);
	if (!mark[u]) t[i].q.push(DNode(u, dep));
	for (int j = head1[u]; ~j; j = nxt1[j]) {
		int v = to1[j], w = len1[j];
		if (j == (x ^ 1) || vis[j]) continue;
		setmide(v, j, i, dep + w);
		siz[u] += siz[v];
	}
	if (ckmin(mx, max(siz[u], tot - siz[u]))) mide = x;
}
void solve(int i, int u) {
	t[i].rt = u;
	tot = siz[u], mx = INF; setmide(u, -1, i, 0);
	if (~mide) {
		int p1 = to1[mide], p2 = to1[mide ^ 1];
		siz[p2] = siz[u] - siz[p1];
		t[i].lc = m++, t[i].rc = m++;
		t[i].w = len1[mide];
		vis[mide] = vis[mide ^ 1] = true;
		solve(t[i].lc, p1);
		solve(t[i].rc, p2);
	}
	pushup(i);
}

void modify(int u) {
	mark[u] ^= 1;
	for (int i = head[u]; ~i; i = nxt[i]) {
		int v = to[i], w = len[i];
		if (!mark[u]) t[v].q.push(DNode(u, w));
		pushup(v);
	}
}

int main() {
	memset(head, -1, sizeof(head));
	memset(head1, -1, sizeof(head1));
	
	scanf("%d", &n);
	for (int i = 1; i < n; ++i) {
		int a, b, c; scanf("%d%d%d", &a, &b, &c);
		add(a, b, c); add(b, a, c);
	}
	
	n1 = n; rebuild(1, 0);
	memset(head, -1, sizeof(head)); js = 0;
	siz[1] = n1; solve(m++, 1);
	
	scanf("%d", &q);
	while (q--) {
		scanf("%s", op);
		if (op[0] == 'A') {
			if (~t[0].ans) printf("%d\n", t[0].ans);
			else printf("They have disappeared.\n");
		}
		else {
			int tmp; scanf("%d", &tmp);
			modify(tmp);
		}
	}
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值