spoj qtree4 边分治

题目大意:树上白点最长距离。。。修改点颜色。。。

大致做法就是边分治,边的两边维护最长距离记子边答案,一路更新,一次操作 O(logn * logn) (堆和树高)。。。

写一写这种题真心锻炼代码能力。。。各种大常数不然调试不能。。。

lyp 把 kAc 卡成 WA30 的神级数据都可以过,但是始终交不过 spoj。。。期待有更新。。。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>
#include <set>
#include <queue>
#include <algorithm>
using namespace std;
#define FOR(i, j, k) for (i = (j); i <= (k); ++ i)
#define ROF(i, j, k) for (i = (j); i >= (k); -- i)
#define FER(i, j, k) for (i = j[k]; i; i = i->n)
#define maxn 200005

int vessle[maxn * 21], * head = vessle, temp;
int * new_array(int size) {return (head += size) - size;}
struct heap {
	int * h, tot;
	heap (int size) {h = new_array(size + 1), tot = 0;}
	void up(int), down(int), push(int), pop(int);
	int top();
};
struct da {int s, t, w; da * n, * f, * l, * r; heap * h; int maxi;};
da das[maxn * 2], * adj = das + 1, * edge[maxn];
struct ed {int t, w; ed * n;};
ed eds[maxn], * adj0 = eds, * edge0[maxn];
int n, Q, size, result; da * centre;
int deg[maxn], mark[maxn];
int dist[21][maxn], posi[21][maxn];/*too small*/
int * dis, * pos; heap * he;
da * father[maxn]; bool color[maxn];

void heap :: up(int p)
{
	for (; p >> 1 && dis[h[p]] > dis[h[p >> 1]]; p >>= 1)
		swap(h[p], h[p >> 1]), pos[h[p]] = p;
	pos[h[p]] = p;
}

void heap :: down(int p)
{
	for (int q; p << 1 <= tot; p = q)
		{
			if (p << 1 == tot) q = tot;
			else if (dis[h[p << 1]] > dis[h[p << 1 | 1]])
				q = p << 1; else q = p << 1 | 1;
			if (dis[h[q]] > dis[h[p]]) swap(h[q], h[p]), pos[h[p]] = p;
			else break;
		}
	pos[h[p]] = p;
}

void heap :: push(int k) {h[++ tot] = k; pos[k] = tot; up(tot);}

void heap :: pop(int k)
{
	h[pos[k]] = h[tot], pos[h[tot]] = pos[k], -- tot, down(pos[k]);
}

int heap :: top() {return dis[h[1]];}

int dfs3(da * fr)
{
	int res = 1; da * e;
	if (he && ! color[fr->t]) he->push(fr->t);
	FER (e, edge, fr->t) if (! e->h && e->t != fr->s)
		dis[e->t] = dis[fr->t] + e->w, res += dfs3(e);
	if (min(res, size - res) > min(result, size - result))
		result = res, centre = fr;
	return res;
}

void update(da * fr)
{
	fr->maxi = max(fr->l->maxi, fr->r->maxi);
	if (fr->l->h->tot && fr->r->h->tot)
		fr->maxi = max(fr->maxi, fr->l->h->top() + fr->l->w + fr->r->h->top());
}

void divide(da * fr, int dep)
{
	dis = dist[dep], pos = posi[dep], he = fr->h;
	if (size <= 1)
		{
			if (color[fr->t]) return;
			father[fr->t] = fr, mark[fr->t] = dep, he->push(fr->t);
			return;
		}
	result = 0, dfs3(fr);
	da * c1 = centre, * c2 = das + ((c1 - das) ^ 1);
	int s1 = result, s2 = size - result;
	c1->h = new heap(s1), c2->h = new heap(s2);
	c1->f = c2->f = fr, fr->l = c1, fr->r = c2;
	size = s1, divide(c1, dep + 1);
	size = s2, divide(c2, dep + 1);
	update(fr), dis = dist[dep];
}

void dfs2(int u)
{
	ed * e;
	if (deg[u] > 2)
		{
			edge0[++ n] = edge0[u]->n, edge0[u]->n = 0;/*too much*/
			* (++ adj0) = (ed) {n, 0, edge0[u]}, edge0[u] = adj0;
			deg[n] = deg[u] - 1, deg[u] = 2, color[n] = 1;
		}
	FER (e, edge0, u)
		{
			* (++ adj) = (da) {u, e->t, e->w, edge[u]}, edge[u] = adj;
			* (++ adj) = (da) {e->t, u, e->w, edge[e->t]}, edge[e->t] = adj;
			dfs2(e->t);
		}
}

void dfs1(int u, int fa)
{
	da * e;
	FER (e, edge, u) if (e->t != fa)
		{
			* (++ adj0) = (ed) {e->t, e->w, edge0[u]}, edge0[u] = adj0;
			++ deg[u], dfs1(e->t, u);
		}
}

int main()
{
	int i, j, k, l, total;
	scanf("%d", & n), total = n;
	FOR (i, 2, n)
		{
			scanf("%d%d%d", & j, & k, & l);
			* (++ adj) = (da) {j, k, l, edge[j]}, edge[j] = adj;
			* (++ adj) = (da) {k, j, l, edge[k]}, edge[k] = adj;
		}

	dfs1(1, 0);
	memset(edge, 0, sizeof edge), adj = das + 1;
	dfs2(1);
	* (++ adj) = (da) {0, 1}, size = n, divide(adj, 0);

	for (scanf("%d\n", & Q); Q --; getchar())
		if (getchar() == 'A')
			if (total) printf("%d\n", adj->maxi);
			else puts("They have disappeared.");
		else
			{
				scanf("%d", & i);
				bool b = color[i] = ! color[i];
				b ? -- total : ++ total, j = mark[i];
				for (da * e = father[i]; e != adj; update(e = e->f), -- j)
					{
						dis = dist[j], pos = posi[j];
						b ? e->h->pop(i) : e->h->push(i);
					}
			}

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值