1095: [ZJOI2007]Hide 捉迷藏
Time Limit: 40 Sec Memory Limit: 256 MB
Submit: 5415 Solved: 2276
[Submit][Status][Discuss]
Description
捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子。某天,Jiajia、Wind和孩子们决定在家里玩
捉迷藏游戏。他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋
子都互相可达。游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯。在起初的
时候,所有的灯都没有被打开。每一次,孩子们只会躲藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要
求打开某个房间的电灯或者关闭某个房间的电灯。为了评估某一次游戏的复杂性,Jiajia希望知道可能的最远的两
个孩子的距离(即最远的两个关灯房间的距离)。 我们将以如下形式定义每一种操作: C(hange) i 改变第i个房
间的照明状态,若原来打开,则关闭;若原来关闭,则打开。 G(ame) 开始一次游戏,查询最远的两个关灯房间的
距离。
Input
第一行包含一个整数N,表示房间的个数,房间将被编号为1,2,3…N的整数。接下来N-1行每行两个整数a, b,
表示房间a与房间b之间有一条走廊相连。接下来一行包含一个整数Q,表示操作次数。接着Q行,每行一个操作,如
上文所示。
Output
对于每一个操作Game,输出一个非负整数到hide.out,表示最远的两个关灯房间的距离。若只有一个房间是关
着灯的,输出0;若所有房间的灯都开着,输出-1。
思路:
把每次分治的重心连成一棵树,树的深度是logn,
每次修改一个结点只影响它到树根的一条链
这题具体实现的时候要维护三层堆
C.每个重心存所有子树到其距离
B.每个重心存各个子树最大值,即子结点堆C的最大值
A.全局一个堆,维护答案最大值,存每个堆B的最大值和次大值之和
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
vector<int>G[maxn];
int dep[maxn],lg[maxn],f[maxn][22];
int root,now_size,son[maxn];
int vis[maxn],father[maxn],have[maxn];
struct node//手搓一个可以删除任意元素的优先队列。
{
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 Sectop()
{
int tmp1=Top();Pop();int tmp2=Top();
Push(tmp1); return tmp2;
}
int Size() {return q.size()-del.size();}
}c[maxn],d[maxn],ans;
void ADD(node &e)
{
if(e.Size()>1) ans.Push(e.Top()+e.Sectop());
}
void Delete(node &e)
{
if(e.Size()>1) ans.Erase(e.Top()+e.Sectop());
}
void get_dep(int v,int fa)
{
dep[v]=dep[fa]+1;
f[v][0]=fa;
for(int i=1;(1<<i)<=dep[v];i++)
f[v][i]=f[f[v][i-1]][i-1];
for(int i=0;i<G[v].size();i++)
if(G[v][i]!=fa) get_dep(G[v][i],v);
}
int LCA(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
while(dep[x]>dep[y])
{
int h=lg[dep[x]-dep[y]];
x=f[x][h];
}
if(x==y) return x;
for(int i=lg[dep[x]];i>=0;i--)
{
if(f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
}
return f[x][0];
}
void get_root(int v,int fa,int SIZE)
{
son[v]=1;
int ma=0;
for(int i=0;i<G[v].size();i++)
{
int to=G[v][i];
if(to==fa||vis[to]) continue;
get_root(to,v,SIZE);
son[v]+=son[to];
ma=max(ma,son[to]);
}
ma=max(ma,SIZE-son[v]);
if(ma<now_size)
{
now_size=ma;
root=v;
}
}
void work(int v,int fa,int rt)
{
d[root].Push(dep[v]+dep[rt]-2*dep[LCA(v,rt)]);
for(int i=0;i<G[v].size();i++)
{
int to=G[v][i];
if(to==fa||vis[to]) continue;
work(to,v,rt);
}
}
void build(int v,int fa)
{
vis[v]=1;
father[v]=fa;
c[v].Push(0);
work(v,fa,fa);
for(int i=0;i<G[v].size();i++)
{
int to=G[v][i];
if(to==fa||vis[to]) continue;
get_root(root=to,v,now_size=son[to]);
int tmp=root;
build(root,v);
c[v].Push(d[tmp].Top());
}
ADD(c[v]);
}
void turn_on(int x)
{
Delete(c[x]);
c[x].Erase(0);
ADD(c[x]);
for(int i=x;i;i=father[i])
{
int fa=father[i];
Delete(c[fa]);
if(d[i].Size()) c[fa].Erase(d[i].Top());
d[i].Erase(dep[x]+dep[fa]-2*dep[LCA(x,fa)]);
if(d[i].Size()) c[fa].Push(d[i].Top());
ADD(c[fa]);
}
}
void turn_off(int x)
{
Delete(c[x]);
c[x].Push(0);
ADD(c[x]);
for(int i=x;i;i=father[i])
{
int fa=father[i];
Delete(c[fa]);
if(d[i].Size()) c[fa].Erase(d[i].Top());
d[i].Push(dep[x]+dep[fa]-2*dep[LCA(x,fa)]);
c[fa].Push(d[i].Top());
ADD(c[fa]);
}
}
int main()
{
for(int i=2;i<maxn;i++) lg[i]=lg[i/2]+1;
int n;scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y;scanf("%d%d",&x,&y);
G[x].push_back(y);
G[y].push_back(x);
}
get_dep(1,0);
get_root(root=1,0,now_size=n);
build(root,0);
int num=0;
int Q;scanf("%d",&Q);
while(Q--)
{
char t[2];int x;scanf("%s",&t);
if(t[0]=='C')
{
scanf("%d",&x);
if(!have[x]) turn_on(x),have[x]=1,num++;
else turn_off(x),have[x]=0,num--;
}
else
{
if(num==n-1) printf("0\n");
else if(num==n) printf("-1\n");
else printf("%d\n",ans.Top());
}
}
return 0;
}