bzoj1095

借这题讲一下我对动态点分治的理解。

一个树先对它进行普通的点分治,大概就是对于一个根每次找重心,然后对于他的每个孩子,找出子树的重心,我们把该点设成他子树孩子的父亲,这样就构成了一棵新树,动态点分大概就是在这颗新树上进行一些奇奇怪怪的操作。

我们考虑这颗树的高度,因为每个点的父亲是它上一层的重心,所以这棵树的高度是log2级别的,具体证明大概就是,对于每个节点,他的子树大小至多是log2(size),所以至多减log2次就变成1了。

这题,对于每个节点过他的最长没开灯房间的距离就是在所有子树中没开灯房间到该点距离中取最大和次大的,由于要修改,我们对于每个点维护一个数据结构支持插入删除并且我们可以快速查询最大和次大的元素大小。

所以我对每个节点维护一个堆q2,意义就是它的子每棵子树中黑色节点到该点的最大值。

我们还要维护一个堆q1,记点u上一层的重心为v,那么q1[u]中储存的就是u的子树中所有黑点到v的距离,为什么存这个,请往下看。

我们还得维护一个堆q,表示所有节点中堆q2前两大的,那么q中的最大值

对于一个开灯操作,这个房间开灯后影响到的房间就是它的虚拟树上的祖先,设这个房间编号为now,我们对着这个节点往上走,设这个点为x,他在虚拟树上的粑粑为y,则删除q1[x]中中路径长度为(now到x的距离)的点(因为点x开灯了),如果是关灯就加入,然后这必然对q2[y]是有影响的因为它x这棵子树中某个点到他的距离已经变了,然后我们考虑怎么修改,大概就是是q2[y]中删除q1[x]的最大值,然后对q1[x]进行操作,操作完成后把q1[x]中的最大值插入q2[y]中,同时q2[y]的修改也影响到了全局的答案q,对q的修改也是类似的,大概就是在q中删除q2[y]的最大和次大值,然后在q2[y]操作完成之后在q中加入q2[y]的最大和次大值。

写到这想必读者应该意会的q1的内涵了吧。

惊讶地发现一个问题就是动态点分治跑得和暴力差不多快。

#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 100005;
int first[MAXN], next[MAXN << 1], go[MAXN << 1], t, x, y;
int n, m, i, j, k, l, tot, ans, Fa[MAXN];
int dis[MAXN], size[MAXN], root, p, Max = 0;
int top[MAXN], pos[MAXN], fa[MAXN], son[MAXN], dep[MAXN];
bool vis[MAXN], ok[MAXN];
char c[10];
struct sb{
	priority_queue <int> a, b;
	inline void insert(int x)
	{
		a.push(x);
	}
	inline void delet(int x)
	{
		b.push(x);
	}
	inline int Top()
	{
		while (!b.empty() && a.top() == b.top()) a.pop(), b.pop();
		return a.top();
	}
	inline void Pop()
	{
		while (!b.empty() && a.top() == b.top()) a.pop(), b.pop();
		a.pop();
	}
	inline int Size()
	{
		return a.size() - b.size();
	}
	inline int nexttop()
	{
		if (Size() >= 2)
		{
			int tmd = Top();
			Pop();
			int tot = Top();
			a.push(tmd);
			return tot;
		}
	}
};
sb q1[MAXN], q2[MAXN], q;
inline int get()
{
	char c;
	while ((c = getchar()) < 48 || c > 57);
	int res = c - 48;
	while ((c = getchar()) >= 48 && c <= 57)
		res = res * 10 + c - 48;
	return res;
}
inline void add(const int &x, const int &y)
{
	next[++t] = first[x]; first[x] = t; go[t] = y;
}
inline void DFS(int now, int fat)
{
	size[now] = 1;
	int son1 = 0, son2 = 0;
	for(int i = first[now]; i; i = next[i])
		if (go[i] != fat)
		{
			dep[go[i]] = dep[now] + 1;
			fa[go[i]] = now;
			DFS(go[i], now);
			size[now] += size[go[i]];
			if (size[go[i]] > son1) son1 = size[go[i]], son2 = go[i];
		}
	son[now] = son2;
}
inline void DFS1(int now)
{
	pos[now] = ++t;
	if (son[now])
	{
		top[son[now]] = top[now];
		DFS1(son[now]);
	}
	for(int i = first[now]; i; i = next[i])
		if (!pos[go[i]])
		{
			top[go[i]] = go[i];
			DFS1(go[i]);
		}
}
inline int getdis(int x, int y)
{
	int xx = x, yy = y;
	while (top[x] != top[y])
	{
		if (dep[top[x]] < dep[top[y]]) swap(x, y);
		x = fa[top[x]];
	}
	int lca = 0;
	if (dep[x] > dep[y]) lca = y;
	else lca = x;
	return dep[xx] + dep[yy] - (dep[lca] << 1);
}
inline int getroot(int now, int fa, int jj)
{
	size[now] = 1;
	int pyz = 0;
	for(int i = first[now]; i; i = next[i])
		if (go[i] != fa && !vis[go[i]]) size[now] += getroot(go[i], now, jj), pyz = max(pyz, size[go[i]]);
	pyz = max(pyz, jj - size[now]);
	if (pyz < p) p = pyz, root = now;
	return size[now];
}
inline void dfs(int now, int fa)
{
	Max = max(Max, dis[now]);
	for(int i = first[now]; i; i = next[i])
		if (go[i] != fa && !vis[go[i]])
		{
			dis[go[i]] = 1 + dis[now];
			dfs(go[i], now);
		}
}
inline void cha(int now, int fa, int wh)
{
	if (wh) q1[wh].insert(dis[now]);
	for(int i = first[now]; i; i = next[i])
		if (fa != go[i] && !vis[go[i]]) cha(go[i], now, wh);
}
inline void solve(int now)
{
	vis[now] = 1;
	for(int i = first[now]; i; i = next[i])
		if (!vis[go[i]])
		{
			dis[go[i]] = 1;
			Max = 1;
			dfs(go[i], now);
			q2[now].insert(Max);
		}
	if (q2[now].Size() >= 2) q.insert(q2[now].Top() + q2[now].nexttop());
	for(int i = first[now]; i; i = next[i])
		if (!vis[go[i]])
		{
			p = 2147483647;
			getroot(go[i], now, size[go[i]]);
			Fa[root] = now;
			cha(go[i], now, root);
			solve(root);
		}
}
inline void turnon(int x)
{
	for(int i = x; Fa[i]; i = Fa[i])
	{
		if (q2[Fa[i]].Size() >= 2) q.delet(q2[Fa[i]].Top() + q2[Fa[i]].nexttop());
		q2[Fa[i]].delet(q1[i].Top());
		q1[i].delet(getdis(x, Fa[i]));
		if (q1[i].Size()) q2[Fa[i]].insert(q1[i].Top());
		if (q2[Fa[i]].Size() >= 2) q.insert(q2[Fa[i]].Top() + q2[Fa[i]].nexttop());
	}
}
inline void turnoff(int x)
{
	for(int i = x; Fa[i]; i = Fa[i])
	{
		if (q2[Fa[i]].Size() >= 2) q.delet(q2[Fa[i]].Top() + q2[Fa[i]].nexttop());
		if (q1[i].Size()) q2[Fa[i]].delet(q1[i].Top());
		q1[i].insert(getdis(x, Fa[i]));
		q2[Fa[i]].insert(q1[i].Top());
		if (q2[Fa[i]].Size() >= 2) q.insert(q2[Fa[i]].Top() + q2[Fa[i]].nexttop());
	}
}
int main()
{
	freopen("hide.in", "r", stdin);
	freopen("hide.out", "w", stdout);
	n = get();
	for(i = 1; i < n; i ++)
	{
		x = get(); y = get();
		add(x, y);
		add(y, x);
	}
	p = 2147483647;
	getroot(1, 0, n);
	solve(root);
	t = 0;
	DFS(1, 0);
	top[1] = 1;
	DFS1(1);
	int cnt = n;
	m = get();
	while (m --)
	{
		scanf("%s", c);
		if (c[0] == 'G')
		{
			if (cnt <= 1) printf("%d\n", cnt - 1);
			else printf("%d\n", q.Top());
		}
		else
		{
			x = get();
			if (!ok[x]) cnt --, ok[x] = 1, turnon(x);
			else ok[x] = 0, cnt ++, turnoff(x);
		}
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值