1095: [ZJOI2007]Hide 捉迷藏(动态点分治维护路径相关问题 + set)

在这里插入图片描述


学了动态点分治确实更理解静态点分治的可行性了

这题是处理路径问题。对于寻找最远的两个点的距离,在静态点分治上,可以每次对当前重心的子树进行暴力合并(拿出子树中深度最深的点和前面得到的最大深度合并得到经过这个重心的答案,并维护这个最大深度值),路径最终都会经过某个重心,考虑动态点分治,用类似的方法来得到答案。

在点分树上每个结点,用一个 set (当然也可以用堆)维护它的子节点的子树中与它的最大距离,对于每个重心,经过这个重心的最长路径是最大值与次大值之和,最终的答案就是这些答案里的最大值,考虑用另外一个 set 维护每个重心的答案,每次询问取最大值。

当一个节点状态改变时,如何维护第一个 set ?直接维护不太容易维护,因为向上修改时,并不知道它来自哪颗子树,也就无法确定是否需要替换或删除。

要想知道是哪棵子树的贡献有变化,需要再维护一个 set ,用来维护点分树上每个节点的子树节点与这个节点的父节点的距离,第一个 set 就是在每个儿子的第三个 set 取一个最大值,第三个 set 比较容易维护,当第三个 set 有修改时,先删除掉第三个 set 对第一个 set 的贡献,同理第一个 set 修改时要先删掉对第二个 set 的贡献,修改完再加回,略为繁琐。

复杂度貌似大于 n log ⁡ 2 n n\log^2n nlog2n,像是 n log ⁡ n ∗ log ⁡ ( n log ⁡ n ) n\log n*\log(n\log n) nlognlog(nlogn)


代码丑陋就连氧气优化也救不了了
代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 5;
typedef long long ll;
#define pii pair<int,ll>
#define fir first
#define sec second
#define lowbit(i) (i & (-i))
struct Graph {
	int head[maxn], to[maxn << 1], cnt, nxt[maxn << 1], w[maxn << 1];
	void init() {
		memset(head,-1,sizeof head);
		cnt = 0;
	}
	void add(int u,int v,int c) {
		to[cnt] = v;
		nxt[cnt] = head[u];
		w[cnt] = c;
		head[u] = cnt++;
		
		to[cnt] = u;
		nxt[cnt] = head[v];
		w[cnt] = c;
		head[v] = cnt++;
	}
}G;
multiset<int> val[maxn],fval[maxn],ANS;					 
int st[maxn * 5][30],d[maxn],cnt,a[maxn];
int fir[maxn],vis[maxn],f[maxn],root,sz[maxn],siz,p[maxn];
int n,q,RT,A;
inline int read()
{
	int x=0,f=1;char ch;
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
void dfs1(int u,int fa) {			//预处理深度,st表,以及欧拉序 
	fir[u] = ++cnt; st[cnt][0] = u;
	for (int i = G.head[u]; i + 1; i = G.nxt[i]) {
		int v = G.to[i];
		if (v == fa) continue;
		d[v] = d[u] + G.w[i];
		dfs1(v,u);
		st[++cnt][0] = u;
	}
}
void dfs2(int u,int fa) {			//求一棵子树的重心 
	sz[u] = 1; f[u] = 0;
	for (int i = G.head[u]; i + 1; i = G.nxt[i]) {
		int v = G.to[i];
		if (v == fa || vis[v]) continue;
		dfs2(v,u);
		sz[u] += sz[v];
		if (sz[v] > f[u]) f[u] = sz[v];
	}
	if (siz - sz[u] > f[u]) f[u] = siz - sz[u];
	if (!root || f[u] < f[root]) root = u;
}
void dfs3(int u,int fa) { 			//点分构建点分树 
	vis[u] = 1;
	int all = siz;
	for (int i = G.head[u]; i + 1; i = G.nxt[i]) {
		int v = G.to[i];
		if (v == fa || vis[v]) continue;
		root = 0, siz = sz[v]; 
		if (siz > sz[u]) siz = all - sz[u];
		dfs2(v,u);
		p[root] = u;	//构建点分树
		dfs3(root,u); 
	}
}
int cal(int u,int v) {
	return d[u] < d[v] ? u : v;
}
int getlca(int x,int y) {
	if (fir[x] > fir[y]) swap(x,y);
	int p = log2(fir[y] - fir[x] + 1);
	return cal(st[fir[x]][p],st[fir[y] - (1 << p) + 1][p]);
}
int getdis(int x,int y) {
	return d[x] + d[y] - 2 * d[getlca(x,y)];
}
void prework() {
	dfs1(1,0);
	for (int i = 1; i <= log2(cnt); i++)
		for (int j = 1; j + (1 << i) - 1 <= cnt; j++)
			st[j][i] = cal(st[j][i - 1],st[j + (1 << (i - 1))][i - 1]);
	root = 0, siz = n; dfs2(1,0);
	RT = root; dfs3(root,0);		//保留第一次的根节点,并构建点分树 
}
void add(int x) {
	if (val[x].size() >= 2) {
		set<int> :: iterator tp = val[x].begin(); tp++;
		set<int> :: iterator t = ANS.lower_bound(*val[x].begin() + *tp);
		ANS.erase(t);
	}
	val[x].insert(0);
	if (val[x].size() >= 2) {
		set<int> :: iterator p = val[x].begin(); p++;
		ANS.insert(*val[x].begin() + *p);
	}
	for (int i = x; p[i]; i = p[i]) {
		if (val[p[i]].size() >= 2) {
			set<int> :: iterator tp = val[p[i]].begin(); tp++;
			set<int> :: iterator t = ANS.lower_bound(*val[p[i]].begin() + *tp);
			ANS.erase(t);
		}
		if (fval[i].size()) {
			set<int> :: iterator t = fval[i].begin();
			set<int> :: iterator it = val[p[i]].lower_bound(*t);
			val[p[i]].erase(it);
		}
		fval[i].insert(-1 * getdis(x,p[i]));
		if (fval[i].size()) {
			set<int> :: iterator t = fval[i].begin();
			val[p[i]].insert(*t);
		}
		if (val[p[i]].size() >= 2) {
			set<int> :: iterator tp = val[p[i]].begin(); tp++;
			ANS.insert(*val[p[i]].begin() + *tp);
		}	
	}
}
void del(int x) {
	if (val[x].size() >= 2) {
		set<int> :: iterator tp = val[x].begin(); tp++;
		set<int> :: iterator t = ANS.lower_bound(*val[x].begin() + *tp);
		ANS.erase(t);
	}
	set<int> :: iterator it = val[x].lower_bound(0); val[x].erase(it);		//删掉0
	if (val[x].size() >= 2) {
		set<int> :: iterator p = val[x].begin(); p++;
		ANS.insert(*val[x].begin() + *p);
	}
	for (int i = x; p[i]; i = p[i]) {
		if (val[p[i]].size() >= 2) {
			set<int> :: iterator tp = val[p[i]].begin(); tp++;
			set<int> :: iterator t = ANS.lower_bound(*val[p[i]].begin() + *tp);
			ANS.erase(t);
		}
		if (fval[i].size()) {
			set<int> :: iterator t = fval[i].begin();
			set<int> :: iterator it = val[p[i]].lower_bound(*t);
			val[p[i]].erase(it);
		}
		set<int> :: iterator tp = fval[i].lower_bound(-1 * getdis(x,p[i])); fval[i].erase(tp);
		if (fval[i].size()) {
			set<int> :: iterator t = fval[i].begin();
			val[p[i]].insert(*t);
		}
		if (val[p[i]].size() >= 2) {
			set<int> :: iterator tp = val[p[i]].begin(); tp++;
			ANS.insert(*val[p[i]].begin() + *tp);
		}	
	} 
}
char op[10];
int main() {
	n = read();
	G.init();
	for (int i = 1; i < n; i++) {
		int u,v; u = read(); v = read();
		G.add(u,v,1);
	}
	prework();
	for (int i = 1; i <= n; i++) {
		a[i] = 0; add(i);
	}
	q = read();
	int tot = n;
	while (q--) {
		scanf("%s",op);
		if (op[0] == 'G') {
			if (tot == 1) puts("0");
			else if (tot == 0) puts("-1");
			else {
				set<int> :: iterator t = ANS.begin();
				printf("%d\n",(*t) * -1);
			}
		} else {
			int pos = read();
			if (a[pos] == 0) del(pos), tot--;
			else add(pos), tot++;
			a[pos] ^= 1;
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值