BZOJ1095 [ZJOI2007]捉迷藏 动态点分治

每次修改一个点的黑白状态,询问树上最远黑点距离

拿这个题做动态点分治模板题:(%%%PoPoQQQ大爷)

点分治的过程是对树块找重心之后分成多个小树块,降低规模分别处理的过程,把链的信息收到其中“最高重心”上,从所有的重心处像分治中的不同子树索取到重心的链,就可以覆盖所有链的信息。动态点分治就像把序列分治变成线段树一样,在分治的架子上加了信息维护,实现树链信息维护与查询。

需要什么?

每个重心需要其每个分离子树到它的信息(很重要,否则形成链的重复部分,并且还需要一个自己到自己的空信息维护单链上来的信息)

每个重心需要它到父分治块的信息

全局需要维护每个重心的信息

因此,考虑问题的静态版本,需要维护每个节点每个子树内的最长链,那么为了修改,最大化应换成堆维护。

每个重心维护一个堆,表示其分离子树中每个到它的最长链

每个重心维护一个堆,表示其块内到父重心的最长链

全局维护一个堆,表示每个重心处的最长链

修改时只要从一个节点作为重心的块开始向上修改,就可以遍历到所有包含它的块,修改到父重心的信息,修改父重心的信息

注意开始时的节点的分离子树那个堆要插一个0表示单链,而改掉节点值的时候0的存在性也要相应地变化。

堆也比较有技巧:封装双堆分别为值和删了的值,取的时候两堆顶相同则pop,相当于变种删除标记

LCA倍增写错:要返回p[x][0](QAQ)

#include<queue>
#include<cstdio>
#include<iostream>
using namespace std;
const int Mn=100005;
int cnt=0,h[Mn],n,m,vst[Mn],maxx,tg,s[Mn],sz,prt[Mn];
int d[Mn],p[Mn][21],co[Mn];
struct Priority_Queue{
	priority_queue<int>q,del;
	void push(int x){q.push(x);}
	void erase(int x){del.push(x);}
	int top(){
		while(del.size()&&del.top()==q.top())
		 {del.pop();q.pop();}
		return q.top();
	}
	void Pop(){
		while(del.size()&&del.top()==q.top())
		 {del.pop();q.pop();}
		q.pop();
	}
	int sec_top(){
		int tmp=top();Pop();
		int se=top();push(tmp);
		return se;
	}
	int size(){return q.size()-del.size();}
}c[Mn],f[Mn],ans;
struct Edge{int to,next;}w[Mn*2];
void AddEdge(int x,int y){w[++cnt]=(Edge){y,h[x]};h[x]=cnt;}
void DFS(int x,int fa){
	 int j,y;
	 p[x][0]=fa;
	 for(j=1;j<=20;j++)p[x][j]=p[p[x][j-1]][j-1];
	 for(j=h[x];j;j=w[j].next){
	 	y=w[j].to;
	 	if(y==fa)continue;
	 	d[y]=d[x]+1;
	 	DFS(y,x);
	 }
}
void Insert(Priority_Queue &s){
	 if(s.size()>1)ans.push(s.top()+s.sec_top());
}
void Erase(Priority_Queue &s){
	 if(s.size()>1)ans.erase(s.top()+s.sec_top());
}
void init(){
	 int i,x,y;
	 scanf("%d",&n);
	 for(i=1;i<n;i++){
	 	scanf("%d%d",&x,&y);
	 	AddEdge(x,y);AddEdge(y,x);
	 }
	 d[1]=1;DFS(1,0);
}
void DP(int x,int fa){
	 int j,y;s[x]=1;
	 for(j=h[x];j;j=w[j].next){
	 	y=w[j].to;
	 	if(y==fa||vst[y])continue;
	 	DP(y,x);
	 	s[x]+=s[y];
	 }
}
void Biggest(int x,int fa){
	 int j,y,mx=0;
	 for(j=h[x];j;j=w[j].next){
	 	y=w[j].to;
	 	if(y==fa||vst[y])continue;
	 	Biggest(y,x);
	 	mx=max(mx,s[y]);
	 }
	 if(maxx>max(mx,sz-s[x])){
	 	maxx=max(mx,sz-s[x]);
	 	tg=x;
	 }
}
int FindCen(int x){
	maxx=n+1;tg=0;
	DP(x,0);
	sz=s[x];
	Biggest(x,0);
	return tg;
}
int LCA(int x,int y){
	int j;
	if(d[x]<d[y])swap(x,y);
	for(j=20;j>=0;j--)
	  if(d[p[x][j]]>=d[y])x=p[x][j];
	if(x==y)return x;
	for(j=20;j>=0;j--)
	  if(p[x][j]!=p[y][j])
	   {x=p[x][j];y=p[y][j];}
	return p[x][0];
}
int Dis(int x,int y){return d[x]+d[y]-2*d[LCA(x,y)];}
void work(int x,int fa,int Gra){
	 int j,y;
	 f[Gra].push(Dis(x,prt[Gra]));
	 for(j=h[x];j;j=w[j].next){
	 	y=w[j].to;
	 	if(y==fa||vst[y])continue;
	 	work(y,x,Gra);
	 }
}
int DivOnT(int x,int fa){
	int j,y,G,Gy;
	G=FindCen(x);prt[G]=fa;
	work(G,0,G);
	vst[G]=1;
	c[G].push(0);
	for(j=h[G];j;j=w[j].next){
		y=w[j].to;
		if(!vst[y]){
			Gy=DivOnT(y,G);
			c[G].push(f[Gy].top());
		}
	}
	Insert(c[G]);
	return G;
}
void Light(int x){
	 Erase(c[x]);
	 c[x].erase(0);
	 Insert(c[x]);
	 for(int y=x;prt[y];y=prt[y]){
		Erase(c[prt[y]]);
		if(f[y].size())c[prt[y]].erase(f[y].top());
		f[y].erase(Dis(x,prt[y]));
		if(f[y].size())c[prt[y]].push(f[y].top());
		Insert(c[prt[y]]);
	 }
}
void LiOut(int x){
	 Erase(c[x]);
     c[x].push(0);
	 Insert(c[x]);
	 for(int y=x;prt[y];y=prt[y]){
		Erase(c[prt[y]]);
		if(f[y].size())c[prt[y]].erase(f[y].top());
		f[y].push(Dis(x,prt[y]));
		if(f[y].size())c[prt[y]].push(f[y].top());
	 	Insert(c[prt[y]]);
	 }
}
void solve(){
	 int i,x;char ch[5];
	 cnt=n;
	 scanf("%d",&m);
	 for(i=1;i<=m;i++){
	 	scanf("%s",ch);
	 	if(ch[0]=='G'){
	 	  if(cnt<=1)printf("%d\n",cnt-1);
		  else printf("%d\n",ans.top());
		}
	 	else{
	 		scanf("%d",&x);
	 		if(!co[x]){cnt--;Light(x);co[x]=1;}
			else{cnt++;LiOut(x);co[x]=0;}
		}
	 }
}
int main(){
    init();  
    DivOnT(1,0);
    solve();
	return 0;
}


你可能感兴趣的:(动态点分治)