[BZOJ 1095][ZJOI2007]Hide 捉迷藏

[BZOJ 1095][ZJOI2007]Hide 捉迷藏

题意

给定一棵 \(n\) 个点的树以及 \(q\) 次操作. 操作有两种, 一种是求有效点之间的最大距离, 另一种是将一个点的有效状态反转.

\(n\le 1\times 10^5,q\le 5\times 10^5\).

题解

好像老老实实写动态点分的在榜上都垫底了qaq

我们可以发现这个求两点之间最大距离非常的点分治, 为了处理修改有效点的问题, 我们需要动态点分治.

首先我们把点分树建出来, 接下来的操作对点分树进行(距离按原树算).

然后对于每个点, 我们记录两个平衡树/堆, 一个 \(len\) 表示这个子树中的所有点到根的父亲的距离, 一个 \(maxd\) 表示各个子结点的 \(len\) 中的最大值. 全局再维护一个 \(all\) 存储每个点的 \(maxd\) 中的最大值与次大值之和.

不难发现每次查询的时候取 \(all\) 中的最大值即是答案.

修改的时候距离用LCA计算, 每次先把当前点可能产生的贡献删除, 修改后再插回去.

初始化的时候有个技巧, DFS当前分治子树的时候 \(dis[root]\) 保存的是父分治子树的根到当前点的距离. 发现这刚好就是我们要算的东西, 直接insert进set就好了.

复杂度 \(O((n+q)\log^2 n)\).

参考代码

#include <bits/stdc++.h>

const int MAXV=1e5+10;
const int MAXE=2e5+10;
typedef std::multiset<int,std::greater<int>> multiset;

struct Edge{
	int from;
	int to;
	Edge* next;
};
Edge E[MAXE];
Edge* head[MAXV];
Edge* topE=E;

int n;
int q;
int root;
int cursize;
int prt[MAXV];
int dis[MAXV];
int top[MAXV];
int son[MAXV];
int size[MAXV];
int pprt[MAXV];
int deep[MAXV];
bool off[MAXV];
bool vis[MAXV];
int msize[MAXV];

multiset all;
multiset len[MAXV];
multiset maxd[MAXV];

char GetCmd();
int ReadInt();
void Solve(int);
int Dis(int,int);
int LCA(int,int);
void DFS(int,int);
void Insert(int,int);
void Modify(int,bool);
void DFS(int,int,int);
void GetRoot(int,int);
int Export(const multiset&);

int main(){
	n=ReadInt();
	for(int i=1;i<n;i++){
		int a=ReadInt(),b=ReadInt();
		Insert(a,b);
		Insert(b,a);
	}
	DFS(1,0,0);
	DFS(1,1);
	cursize=n;
	msize[0]=n;
	GetRoot(1,0);
	Solve(root);
	q=ReadInt();
	while(q--){
		if(GetCmd()=='C'){
			int x=ReadInt();
			Modify(x,off[x]^=1);
		}
		else
			printf("%d\n",*all.begin());
	}
	return 0;
}

void Modify(int x,bool opt){
	if(maxd[x].size()>1)
		all.erase(all.find(Export(maxd[x])));
	if(opt)
		maxd[x].erase(maxd[x].find(0));
	else
		maxd[x].insert(0);
	if(maxd[x].size()>1)
		all.insert(Export(maxd[x]));
	for(int p=x;prt[p]!=0;p=prt[p]){
		if(maxd[prt[p]].size()>1)
			all.erase(all.find(Export(maxd[prt[p]])));
		if(!len[p].empty())
			maxd[prt[p]].erase(maxd[prt[p]].find(*len[p].begin()));
		if(opt)
			len[p].erase(len[p].find(Dis(prt[p],x)));
		else
			len[p].insert(Dis(prt[p],x));
		if(!len[p].empty())
			maxd[prt[p]].insert(*len[p].begin());
		if(maxd[prt[p]].size()>1)
			all.insert(Export(maxd[prt[p]]));
	}
}

void DFS(int root,int prt,multiset& len){
	if(dis[root]!=0)
		len.insert(dis[root]);
	dis[root]=dis[prt]+1;
	for(Edge* i=head[root];i!=NULL;i=i->next)
		if(i->to!=prt&&!vis[i->to])
			DFS(i->to,root,len);
}

void Solve(int root){
	vis[root]=true;
	if(dis[root]!=0)
		len[root].insert(dis[root]);
	dis[root]=0;
	for(Edge* i=head[root];i!=NULL;i=i->next){
		if(!vis[i->to])
			DFS(i->to,root,len[root]);
	}
	maxd[root].insert(0);
	for(Edge* i=head[root];i!=NULL;i=i->next){
		if(!vis[i->to]){
			::root=0;
			cursize=size[i->to];
			GetRoot(i->to,root);
			int son=::root;
			prt[son]=root;
			Solve(son);
			if(!len[son].empty())
				maxd[root].insert(*len[son].begin());
		}
	}
	if(maxd[root].size()>1)
		all.insert(Export(maxd[root]));
}

void GetRoot(int root,int prt){
	size[root]=1;
	msize[root]=0;
	for(Edge* i=head[root];i!=NULL;i=i->next){
		if(!vis[i->to]&&i->to!=prt){
			GetRoot(i->to,root);
			size[root]+=size[i->to];
			msize[root]=std::max(msize[root],size[i->to]);
		}
	}
	msize[root]=std::max(msize[root],cursize-size[root]);
	if(msize[root]<msize[::root])
		::root=root;
}

inline void Insert(int from,int to){
	topE->from=from;
	topE->to=to;
	topE->next=head[from];
	head[from]=topE++;
}

void DFS(int root,int prt,int deep){
	size[root]=1;
	pprt[root]=prt;
	::deep[root]=deep;
	for(Edge* i=head[root];i!=NULL;i=i->next){
		if(i->to!=prt){
			DFS(i->to,root,deep+1);
			size[root]+=size[i->to];
			if(size[i->to]>size[son[root]])
				son[root]=i->to;
		}
	}
}

void DFS(int root,int top){
	::top[root]=top;
	if(son[root])
		DFS(son[root],top);
	for(Edge* i=head[root];i!=NULL;i=i->next)
		if(i->to!=pprt[root]&&i->to!=son[root])
			DFS(i->to,i->to);
}

inline int Dis(int x,int y){
	return deep[x]+deep[y]-2*(deep[LCA(x,y)]);
}

inline int LCA(int x,int y){
	while(top[x]!=top[y]){
		if(deep[top[x]]<deep[top[y]])
			std::swap(x,y);
		x=pprt[top[x]];
	}
	if(deep[x]>deep[y])
		std::swap(x,y);
	return x;
}

inline int ReadInt(){
	int x=0;
	register char ch=getchar();
	while(!isdigit(ch))
		ch=getchar();
	while(isdigit(ch)){
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x;
}

inline char GetCmd(){
	register char ch=getchar();
	while(ch!='C'&&ch!='G')
		ch=getchar();
	return ch;
}

inline int Export(const multiset& s){
	auto it=s.begin();
	++it;
	return *s.begin()+*it;
}

posted @ 2019-03-10 21:45  rvalue  阅读(205)  评论(0编辑  收藏  举报