bzoj-1095 Hide 捉迷藏

题意:

给出一棵树,初始所有结点都是白的;

操作有两种:

1.修改一个结点的颜色;

2.查询当前树上任意两个白点的最远距离;

n<=100000,m<=500000;


题解:

算是把当年挖下的一个大坑填了一半吧。。。

SPOJ的QTREE4又爆栈又卡常,姿势太丑过不去就先算了= =

树上最远点对的经典解法是用树的点分治来搞;

这次多了修改,那么就要动态的维护这个点分治;

具体来说,首先将树分治,然后从下一层分治结构的重心向上一层重心连边,组成一个分治树;

这个分治树是十分优雅的!首先它的层数不会超过log层,并且我们需要统计的信息由一个无根树的信息转化成了一颗子树的信息;

可以想到,每一次修改的时候直接对某点以及其祖先进行修改就可以了,复杂度是O(logn*(操作复杂度));

对于这道题具体来说,我们要维护最远点对,那就对于每个分治结构维护从当前重心出发,到某颗子树的最长路(注意是到某个子树中,即每个子树仅算一次);

为了维护这个“跨过重心”,我们还需要利用下一层分治结构的信息;

也就是再维护一个从某分治结构的任意点到上一层分治结构重心的最长路;

然后为了方便求解答案,再维护出所有分治结构最长链的最大值就可以了;

这三个东西都是用堆来维护,堆的修改复杂度是O(logn),因此总时间复杂度为O(nlog^2n+mlog^2n);

空间上,因为分治结构数是O(n)的  (和线段树的空间类似),所以第一个和第三个堆总共都只有n个元素;

第二个堆和树套树一样的分析,每一层所有分治结构共有n个结点,总共不超过log层;

所以空间是O(nlogn)的;


因为我太弱所以不会太多奇奇怪怪的堆姿势,于是就上了可并堆;

因为我比较懒,所以最后一个堆直接用set实现了;

因为一开始YY错了的原因,所以代码非常丑非常长。。。


代码:


#include<set>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 110000
#define iter multiset<int>::iterator
using namespace std;
struct Heap
{
	int l,r,fa,val,dis;
}tr[N*20];
int next[N<<1],to[N<<1],head[N],ce,tot;
int fa[N],deep[N],size[N],root[N],ma[N],self[N],art[N],node[N][20];
char str[10];
bool ban[N];
multiset<int>st;
int merge(int x,int y)
{
	if(!x||!y)	return x+y;
	if(tr[x].val<tr[y].val)
		swap(x,y);
	tr[x].r=merge(tr[x].r,y);
	tr[tr[x].r].fa=x;
	if(tr[tr[x].l].dis<tr[tr[x].r].dis)
		swap(tr[x].l,tr[x].r);
	tr[x].dis=tr[tr[x].r].dis+1;
	return x;
}
int calc(int rt)
{
	return tr[rt].val+max(tr[tr[rt].l].val,tr[tr[rt].r].val);
}
void add(int x,int y)
{
	to[++ce]=y;
	next[ce]=head[x];
	head[x]=ce;
}
int getG(int x,int pre,int &g,int n)
{
	int size=1,temp,ma,i;
	for(i=head[x],ma=0;i;i=next[i])
	{
		if(to[i]!=pre&&!ban[to[i]])
		{
			temp=getG(to[i],x,g,n);
			ma=max(ma,temp);
			size+=temp;
		}
	}
	ma=max(ma,n-size);
	if(ma<=n/2)
		g=x;
	return size;
}
void dfs(int x,int pre,int dis,int rt)
{
	node[x][deep[rt]]=++tot;
	tr[tot].val=dis;
	root[rt]=merge(root[rt],tot);
	for(int i=head[x];i;i=next[i])
	{
		if(to[i]!=pre&&!ban[to[i]])
		{
			dfs(to[i],x,dis+1,rt);
		}
	}
}
void Build(int x,int n)
{
	ban[x]=1;
	self[x]=++tot;
	art[x]=tot;
	size[x]=1;
	if(n==1)	return ;
	int i,g,siz;
	for(i=head[x];i;i=next[i])
	{
		if(!ban[to[i]])
		{
			siz=getG(to[i],x,g,n);
			getG(to[i],x,g,siz);
			fa[g]=x;
			deep[g]=deep[x]+1;
			dfs(to[i],x,1,g);
			ma[g]=++tot;
			tr[tot].val=tr[root[g]].val;
			art[x]=merge(art[x],tot);
			size[x]++;
			Build(g,siz);
		}
	}
	if(size[x]>=2)
		st.insert(calc(art[x]));
}
void remove(int y)
{
	if(size[fa[y]]>=2)
		st.erase(st.find(calc(art[fa[y]])));
	if(root[y])
	{
		if(art[fa[y]]!=ma[y])
		{
			if(tr[tr[ma[y]].fa].l==ma[y])
				tr[tr[ma[y]].fa].l=merge(tr[ma[y]].l,tr[ma[y]].r),
				tr[tr[tr[ma[y]].fa].l].fa=tr[ma[y]].fa;
			else
				tr[tr[ma[y]].fa].r=merge(tr[ma[y]].l,tr[ma[y]].r),
				tr[tr[tr[ma[y]].fa].r].fa=tr[ma[y]].fa;
		}
		else
			art[fa[y]]=merge(tr[ma[y]].l,tr[ma[y]].r),
			tr[art[fa[y]]].fa=0;
		size[fa[y]]--;
	}
}
void resume(int y)
{
	if(root[y])
	{
		tr[ma[y]].val=tr[root[y]].val;
		tr[ma[y]].fa=tr[ma[y]].l=tr[ma[y]].r=0;
		art[fa[y]]=merge(art[fa[y]],ma[y]);
		size[fa[y]]++;
	}
	if(size[fa[y]]>=2)
		st.insert(calc(art[fa[y]]));
}
void remove2(int y)
{
	if(size[y]>=2)
		st.erase(st.find(calc(art[y])));
	if(ban[y])
	{
		if(art[y]!=self[y])
		{
			if(tr[tr[self[y]].fa].l==self[y])
				tr[tr[self[y]].fa].l=merge(tr[self[y]].l,tr[self[y]].r),
				tr[tr[tr[self[y]].fa].l].fa=tr[self[y]].fa;
			else
				tr[tr[self[y]].fa].r=merge(tr[self[y]].l,tr[self[y]].r),
				tr[tr[tr[self[y]].fa].r].fa=tr[self[y]].fa;
		}
		else
			art[y]=merge(tr[self[y]].l,tr[self[y]].r),
			tr[art[y]].fa=0;
		size[y]--;
	}
}
void resume2(int y)
{
	if(!ban[y])
	{
		tr[self[y]].val=0;
		tr[self[y]].fa=tr[self[y]].l=tr[self[y]].r=0;
		art[y]=merge(art[y],self[y]);
		size[y]++;
	}
	if(size[y]>=2)
		st.insert(calc(art[y]));
}
int main()
{
	int n,m,i,j,k,x,y,g,now;
	scanf("%d",&n);
	for(i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y),add(y,x);
	}
	tr[0].dis=-1;
	getG(1,0,g,n);
	Build(g,n);
	scanf("%d",&m);
	for(i=1,now=n;i<=m;i++)
	{
		scanf("%s",str);
		if(str[0]=='C')
		{
			scanf("%d",&x);
			if(ban[x])
			{
				remove2(x);
				resume2(x);
				for(y=x;fa[y];y=fa[y])
				{
					remove(y);
					k=node[x][deep[y]];
					if(k!=root[y])
					{
						if(tr[tr[k].fa].l==k)
							tr[tr[k].fa].l=merge(tr[k].l,tr[k].r),
							tr[tr[tr[k].fa].l].fa=tr[k].fa;
						else
							tr[tr[k].fa].r=merge(tr[k].l,tr[k].r),
							tr[tr[tr[k].fa].r].fa=tr[k].fa;
					}
					else
						root[y]=merge(tr[k].l,tr[k].r);
					resume(y);
				}
				ban[x]=0;
				now--;
			}
			else
			{
				remove2(x);
				resume2(x);
				for(y=x;fa[y];y=fa[y])
				{
					remove(y);
					k=node[x][deep[y]];
					tr[k].fa=tr[k].l=tr[k].r=tr[k].dis=0;
					root[y]=merge(root[y],k);
					resume(y);
				}
				ban[x]=1;
				now++;
			}
		}
		else
		{
			if(now==1)
				puts("0");
			else if(!now)
				puts("-1");
			else
			{
				iter it=st.end();
				it--;
				printf("%d\n",*it);
			}
		}
	}
	return 0;
}



你可能感兴趣的:(堆,bzoj,点分治,动态树分治)