2019.01.10 bzoj1095: [ZJOI2007]Hide 捉迷藏(动态点分治)

传送门

蒟蒻真正意义上做的第一道动态点分治!

题意:给一棵最开始所有点都是黑点的树,支持把点的颜色变成从黑/白色变成白/黑色,问当前状态树上两个最远黑点的距离。


思路:

首先考虑不带修改一次点分治怎么做的。

显然对于每个树上的节点ppp可以对它的每一个儿子vvv维护一个静态的集合BvB_vBv​表示vvv子树中所有点到ppp的距离,然后对于ppp这个点可以维护一个静态集合CpC_pCp​来记录所有maxBvmax_{B_v}maxBv​​,因此对于这个子树所有经过点ppp的答案就是CpC_pCp​中的最大值和次大值之和,然后用一个静态集合AAA来维护所有点的答案CiC_iCi​。

这样可以处理静态的。

那么考虑在一个点变化之后,A,B,CA,B,CA,B,C好像都会发生变化,然后如果我们建出点分树的话会发现变化只对于点分树上面这个点到根节点这条路径上面的点有影响。

那么我们将上述的集合变成动态的修改一下就可以了。

代码:

#include<bits/stdc++.h>
#define ri register int
#define pb push_back
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
const int N=200005;
int n,q;
int all,rt,fa[N],siz[N],msiz[N],dep[N],hson[N],top[N],Fa[N];
bool vis[N],del[N];
vector<int>e[N];
struct Que{
	priority_queue<int>a,b;
	inline void push(int x){a.push(x);}
	inline void del(int x){b.push(x);}
	inline int size(){return a.size()-b.size();}
	inline void pop(){while(b.size()&&a.top()==b.top())a.pop(),b.pop();a.pop();}
	inline int top(){while(b.size()&&a.top()==b.top())a.pop(),b.pop();return a.top();}
	inline int stop(){int tmp=top(),ret;return pop(),ret=top(),push(tmp),ret;}
}A,B[N],C[N];
void dfs1(int p){
	siz[p]=1;
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])==Fa[p])continue;
		Fa[v]=p,dep[v]=dep[p]+1,dfs1(v),siz[p]+=siz[v];
		if(siz[v]>siz[hson[p]])hson[p]=v;
	}
}
void dfs2(int p,int tp){
	top[p]=tp;
	if(!hson[p])return;
	dfs2(hson[p],tp);
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])!=Fa[p]&&v!=hson[p])dfs2(v,v);
	}
}
inline int lca(int u,int v){
	while(top[u]^top[v]){
		if(dep[top[u]]<dep[top[v]])swap(u,v);
		u=Fa[top[u]];
	}
	return dep[u]<dep[v]?u:v;
}
inline int dist(int u,int v){return dep[u]+dep[v]-2*dep[lca(u,v)];}
void getroot(int p,int fax){
	siz[p]=1,msiz[p]=0;
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])==fax||vis[v])continue;
		getroot(v,p),siz[p]+=siz[v],msiz[p]=max(msiz[p],siz[v]);
	}
	msiz[p]=max(msiz[p],all-siz[p]);
	if(msiz[p]<msiz[rt])rt=p;
}
void solve(int p,int fat){
	C[rt].push(dist(p,fa[rt]));
	for(ri i=0,v;i<e[p].size();++i){
		if((v=e[p][i])==fat||vis[v])continue;
		solve(v,p);
	}
}
inline void pushans(int p){if(B[p].size()>=2)A.push(B[p].top()+B[p].stop());}
inline void deleans(int p){if(B[p].size()>=2)A.del(B[p].top()+B[p].stop());}
void dfs(int p){
	vis[p]=1,solve(p,0),B[p].push(0);
	for(ri i=0,v;i<e[p].size();++i){
		if(vis[v=e[p][i]])continue;
		all=siz[v],rt=0,getroot(v,0),fa[v=rt]=p,dfs(rt),B[p].push(C[v].top());
	}
	pushans(p);
}
inline void On(int p){
	deleans(p),B[p].del(0),pushans(p);
	for(ri i=p;fa[i];i=fa[i]){
		deleans(fa[i]),B[fa[i]].del(C[i].top()),C[i].del(dist(p,fa[i]));
		if(C[i].size())B[fa[i]].push(C[i].top());
		pushans(fa[i]);
	}
}
inline void Off(int p){
	deleans(p),B[p].push(0),pushans(p);
	for(ri i=p;fa[i];i=fa[i]){
		deleans(fa[i]);
		if(C[i].size())B[fa[i]].del(C[i].top());
		C[i].push(dist(p,fa[i])),B[fa[i]].push(C[i].top()),pushans(fa[i]);
	}
}
int main(){
	freopen("lx.in","r",stdin);
	n=read();
	for(ri i=1,u,v;i<n;++i)u=read(),v=read(),e[u].pb(v),e[v].pb(u);
	dfs1(1),dfs2(1,1);
	msiz[rt=0]=all=n,getroot(1,0),dfs(rt);
	int tot=n;
	for(ri tt=read();tt;--tt){
		char s[2];
		scanf("%s",s);
		if(s[0]=='G'){
			if(tot==1)puts("0");
			else if(tot==0)puts("-1");
			else cout<<A.top()<<'\n';
		}
		else{
			int x=read();
			del[x]^=1;
			if(!del[x])Off(x),++tot;
			else On(x),--tot;
		}
	}
	return 0;
}
上一篇:C#SerialPort如何读取串口数据并显示在TextBox上


下一篇:web-day4