【BZOJ1095】捉迷藏(ZJOI2007)-动态点分治+堆
测试地址:捉迷藏
做法:本题需要用到动态点分治+堆。
一棵树上有黑点和白点,求两个黑点之间的最远距离,如果没有修改,我们完全可以使用DP或者点分治的方法求出。现在的问题是带了修改,那么从DP的角度来考虑就比较难了,所以我们从点分治的角度考虑怎么拓展。
回顾点分治的思路,我们对一棵树找到它的重心,然后递归处理删去这个点后该树分成的所有子树。对于每一棵树,考虑过重心的所有路径,用类似DP的方法可以求出这棵树内过重心的最远距离黑点对。考虑这样一种暴力,每次修改都暴力做一次点分治,这样显然会增大时间复杂度。但考虑到,每次修改只涉及到一个点,也就是说只涉及到一端为这个点的所有路径,我们发现它最多会影响个点上求出的最远点对。至于为什么,请继续往下看。
考虑构造这样一个结构来描述点分治的过程:对于一棵树,先将它的重心作为根,递归处理它的所有子树,并从重心向这些子树的重心分别连边。显然这样连出来的也是一棵树,这棵树的深度最多为。我们把这棵树称为点分树,它是一个描述点分治过程的结构。
我们发现,修改一个点,影响到的点就是它在点分树上的所有祖先,也就是最多影响到个。回顾原先的点分治需要维护什么东西:对于一棵点分树上的子树,维护子树内所有黑点到根的祖先的最远距离;对于点分树上的每一个点,维护几棵子树中最远黑点距离的最大值和次大值;当然,还要维护对于所有的点,求出的最远黑点点对的距离。我们发现这些都是要维护某些值中最大的几个值,用堆来维护即可,至于可定值删除堆的写法详见本人的代码。那么我们每修改一个点,都暴力修改它在点分树上所有祖先的信息,每次修改一个祖先的信息都是的,于是我们就得到了一个时间复杂度为,空间复杂度为的做法,可以通过此题。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
int n,m,first[100010]={0},firstt[100010]={0},tot=0,dep[100010]={0},fa[100010];
int f[100010],mxson[100010],siz[100010],q[100010],totsiz,top;
int dis[100010][20],totvis;
bool vis[100010]={0};
struct edge
{
int v,next;
}e[200010],t[100010];
struct heap
{
priority_queue<int> a,b;
void push(int x) {if (x>=0) a.push(x);}
void del(int x) {if (x>=0) b.push(x);}
int size() {return a.size()-b.size();}
int top()
{
if (!size()) return -1;
while(!b.empty()&&a.top()==b.top()) a.pop(),b.pop();
return a.top();
}
int sum()
{
if (size()<2) return -1;
int A=top();a.pop();
int B=top();push(A);
return A+B;
}
}p[100010],st[100010],ans;
void insert(int a,int b) {e[++tot].v=b,e[tot].next=first[a],first[a]=tot;}
void insertt(int a,int b) {t[++tot].v=b,t[tot].next=firstt[a],firstt[a]=tot;}
void dp(int v,int fa)
{
totsiz++;
q[++top]=v;
mxson[v]=0;
siz[v]=1;
for(int i=first[v];i;i=e[i].next)
if (e[i].v!=fa&&!vis[e[i].v])
{
dp(e[i].v,v);
mxson[v]=max(mxson[v],siz[e[i].v]);
siz[v]+=siz[e[i].v];
}
}
int find(int v)
{
totsiz=top=0;
dp(v,0);
int ans=100000000,ansi;
for(int i=1;i<=top;i++)
if (max(mxson[q[i]],totsiz-siz[q[i]])<ans)
{
ans=max(mxson[q[i]],totsiz-siz[q[i]]);
ansi=q[i];
}
return ansi;
}
void put(int v,int tar,int d,int Fa)
{
dis[v][dep[v]-dep[tar]]=d;
p[tar].push(d);
for(int i=first[v];i;i=e[i].next)
if (e[i].v!=Fa&&!vis[e[i].v]) put(e[i].v,tar,d+1,v);
}
int solve(int v,int Fa)
{
v=find(v);
vis[v]=1;
dep[v]=dep[Fa]+1;
fa[v]=Fa;
for(int i=first[v];i;i=e[i].next)
if (!vis[e[i].v])
{
int nx=solve(e[i].v,v);
insertt(v,nx);
put(e[i].v,nx,1,0);
}
st[v].push(0);
for(int i=firstt[v];i;i=t[i].next)
st[v].push(p[t[i].v].top());
ans.push(st[v].sum());
vis[v]=0;
return v;
}
void turn(int v)
{
vis[v]=!vis[v];
if (vis[v])
{
totvis--;
ans.del(st[v].sum());
st[v].del(0);
ans.push(st[v].sum());
}
else
{
totvis++;
ans.del(st[v].sum());
st[v].push(0);
ans.push(st[v].sum());
}
int h=0,x=v;
while(x)
{
if (fa[x])
{
ans.del(st[fa[x]].sum());
st[fa[x]].del(p[x].top());
}
if (vis[v]) p[x].del(dis[v][h]);
else p[x].push(dis[v][h]);
if (fa[x])
{
st[fa[x]].push(p[x].top());
ans.push(st[fa[x]].sum());
}
h++;
x=fa[x];
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int a,b;
scanf("%d%d",&a,&b);
insert(a,b),insert(b,a);
}
tot=0;
solve(1,0);
totvis=n;
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
char op[3];
scanf("%s",op);
if (op[0]=='G')
{
if (totvis>=2) printf("%d\n",ans.top());
else printf("%d\n",totvis?0:-1);
}
else
{
int x;
scanf("%d",&x);
turn(x);
}
}
return 0;
}