题目描述
Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子。某天,Jiajia、Wind和孩子们决定在家里玩捉迷藏游戏。他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋子都互相可达。
游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯。在起初的时候,所有的灯都没有被打开。每一次,孩子们只会躲藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要求打开某个房间的电灯或者关闭某个房间的电灯。为了评估某一次游戏的复杂性,Jiajia希望知道可能的最远的两个孩子的距离(即最远的两个关灯房间的距离)。
我们将以如下形式定义每一种操作:
C(hange) i 改变第i个房间的照明状态,若原来打开,则关闭;若原来关闭,则打开。
G(ame) 开始一次游戏,查询最远的两个关灯房间的距离。
输入输出格式
输入格式:
第一行包含一个整数N,表示房间的个数,房间将被编号为1,2,3…N的整数。
接下来N-1行每行两个整数a, b,表示房间a与房间b之间有一条走廊相连。
接下来一行包含一个整数Q,表示操作次数。接着Q行,每行一个操作,如上文所示。
输出格式:
对于每一个操作Game,输出一个非负整数到hide.out,表示最远的两个关灯房间的距离。若只有一个房间是关着灯的,输出0;若所有房间的灯都开着,输出-1。
输入输出样例
输入样例#1:
8
1 2
2 3
3 4
3 5
3 6
6 7
6 8
7
G
C 1
G
C 2
G
C 1
G
输出样例#1:
4
3
3
4
说明
对于
20
%
20\%
20% 的数据,
N
≤
50
,
M
≤
100
N ≤50, M ≤100
N≤50,M≤100;
对于
60
%
60\%
60% 的数据,
N
≤
3000
,
M
≤
10000
N ≤3000, M ≤10000
N≤3000,M≤10000;
对于
100
%
100\%
100% 的数据,
N
≤
100000
,
M
≤
500000
N ≤100000, M ≤500000
N≤100000,M≤500000。
分析:
动态树分治模板题。
我们把某个分治中心与他分开的连通块的分治中心连边,这样就有一棵分治树,可以知道树高为
l
o
g
n
logn
logn。
我们对于每个点的每个儿子维护一个堆(set也可以),那么所有儿子的堆顶最大值和次大值就是答案(维护儿子堆顶也要开一个堆)。再给所有的点维护一个堆,表示每个分治中心的答案。
至于求两点距离,可以使用括号序,然后求两点的最小值。可以用rmq解决。
这样预处理就是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)的,查询距离是
O
(
l
o
g
n
)
O(logn)
O(logn)的,因为每次修改要查询
O
(
l
o
g
n
)
O(logn)
O(logn)个距离。而由于每个堆并不是满的,所以修改总操作应该是
O
(
l
o
g
n
)
O(logn)
O(logn)的(这个可以像多项式求逆那样分析)。总复杂度是
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)的,但是常数比较大。
代码:
// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
const int N=100005;
int n,m,fa[N],dep[N],pos[N],rmq[N*2][20],cnt,dfn,last[N],lg[N*2],size[N],mx[N],tot,root;
bool vis[N],clo[N];
struct edge{int to,next;}e[N*2];
struct Heap
{
priority_queue<int> a,b;
void push(int x)
{
a.push(x);
}
void erase(int x)
{
b.push(x);
}
void pop()
{
while (b.size()&&a.top()==b.top()) a.pop(),b.pop();
a.pop();
}
int top()
{
while (b.size()&&a.top()==b.top()) a.pop(),b.pop();
if (!a.size()) return 0;
else return a.top();
}
int size()
{
return a.size()-b.size();
}
int stop()
{
if (size()<2) return 0;
int x=top();pop();
int y=top();push(x);
return y;
}
}a,b[N],c[N];
void addedge(int u,int v)
{
e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;
}
void dfs(int x,int fa)
{
dep[x]=dep[fa]+1;pos[x]=++dfn;rmq[dfn][0]=dep[x];
for (int i=last[x];i;i=e[i].next)
{
if (e[i].to==fa) continue;
dfs(e[i].to,x);
rmq[++dfn][0]=dep[x];
}
}
int get_dis(int x,int y)
{
int l=pos[x],r=pos[y];
if (l>r) swap(l,r);
int len=lg[r-l+1],mn=min(rmq[l][len],rmq[r-(1<<len)+1][len]);
return dep[x]+dep[y]-2*mn;
}
void get_root(int x,int fa)
{
size[x]=1;mx[x]=0;
for (int i=last[x];i;i=e[i].next)
{
if (vis[e[i].to]||e[i].to==fa) continue;
get_root(e[i].to,x);
size[x]+=size[e[i].to];
mx[x]=max(mx[x],size[e[i].to]);
}
mx[x]=max(mx[x],tot-size[x]);
if (!root||mx[x]<mx[root]) root=x;
}
void build(int x,int y)
{
vis[x]=1;fa[x]=y;
for (int i=last[x];i;i=e[i].next)
{
if (vis[e[i].to]) continue;
root=0;tot=size[e[i].to];
get_root(e[i].to,0);
build(root,x);
}
}
void turn_off(int u,int v)
{
if (u==v)
{
c[u].push(0);
if (c[u].size()==2) a.push(c[u].top());
}
if (!fa[u]) return;
int f=fa[u],d=get_dis(v,f),tmp=b[u].top();
b[u].push(d);
if (d>tmp)
{
int mx=c[f].top()+c[f].stop(),size=c[f].size();
if (tmp) c[f].erase(tmp);
c[f].push(d);
int now=c[f].top()+c[f].stop();
if (now>mx)
{
if (size>=2) a.erase(mx);
if (c[f].size()>=2) a.push(now);
}
}
turn_off(f,v);
}
void turn_on(int u,int v)
{
if (u==v)
{
c[u].erase(0);
if (c[u].size()==1) a.erase(c[u].top());
}
if (!fa[u]) return;
int f=fa[u],d=get_dis(v,f),tmp=b[u].top();
b[u].erase(d);
if (d==tmp)
{
int mx=c[f].top()+c[f].stop(),size=c[f].size();
c[f].erase(d);
if (b[u].size()) c[f].push(b[u].top());
int now=c[f].top()+c[f].stop();
if (now<mx)
{
if (size>=2) a.erase(mx);
if (c[f].size()>=2) a.push(now);
}
}
turn_on(f,v);
}
int main()
{
scanf("%d",&n);
for (int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y);
}
dfs(1,0);
for (int i=1;i<=dfn;i++) lg[i]=log(i)/log(2);
for (int j=1;j<=lg[dfn];j++)
for (int i=1;i<=dfn-(1<<j)+1;i++)
rmq[i][j]=min(rmq[i][j-1],rmq[i+(1<<(j-1))][j-1]);
root=0;tot=n;
get_root(1,0);
build(root,0);
for (int i=1;i<=n;i++) clo[i]=1,turn_off(i,i);
scanf("%d",&m);
while (m--)
{
char ch[2];
scanf("%s",ch);
if (ch[0]=='C')
{
int x;
scanf("%d",&x);
if (clo[x]) clo[x]=0,turn_on(x,x);
else clo[x]=1,turn_off(x,x);
}
else printf("%d\n",a.top());
}
}