【题解】Atcoder ABC460F - Farthest Pair Query / Luogu P2056 [ZJOI2007] 捉迷藏
题意
给定一棵 \(n\) 个点的树,每个点分为黑点和白点。初始全为黑,\(q\) 次询问,每次更改一个点,黑变白白变黑,输出两个黑点之间最长的距离。
思路
题目求一个可变点集构成的树的直径。
根据直径的定义,我们可以知道将一个点 \(p\) 加入一棵树后,设旧的树直径端点为 \(a,b\),新的直径端点一定在 \(a,b,p\) 中产生。证明考虑反证法,如果有一条路径 \(u,v\) 中一个端点到 \(p\) 的距离比 \(\operatorname{dis}(a,p),\operatorname{dis}(b,p),\operatorname{dis}(a,b)\) 都要长,那么 \((a,b)\) 就不是旧树上最长。具体证明可以画图分类讨论。
那么我们考虑如果要合并两棵树直径将如何变化。设两树直径为 \((a_1,b_1),(a_2,b_2)\),我们对两个点集互相使用上述定理可以发现,新的直径端点必然产生于 \(a_1,b_1,a_2,b_2\) 之中,共六种情况取最大值。由此,我们就知道了如何合并两个点集的直径。
我们可以用线段树维护点编号序列上的区间信息,一个区间作为一个点集计算他们的直径。有了合并方法我们就可以从叶子一路合并求得整棵树的直径,只需维护区间直径端点和直径长度。从白染黑,将对应叶子修改为 \(0\),反之则赋为负无穷不参与求最值。答案输出整棵树直径。
合并过程中我们需要快速知道四个端点两两之间的长度,也就是要知道他们各自的深度和 LCA 的深度。使用欧拉序可以做到 \(O(n\log n)\) 预处理和 \(O(1)\),足够快速。
总时间复杂度 \(O(n\log n)\)。
实现
这里我们使用构造函数和重载运算符,使代码十分简洁。
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
const int INF=1e9;
int n,q;
int lg[2*N];
int h[N],tot;
struct Node{
int to,nxt;
}e[2*N];
void Add(int u,int v){
tot++;
e[tot].to=v;
e[tot].nxt=h[u];
h[u]=tot;
}
int dep[N],que[2*N];
int dfn[N],idx,f[2*N][30];
void dfs(int u,int fa){
dfn[u]=++idx,que[idx]=u;
dep[u]=dep[fa]+1;
for(int i=h[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa) continue;
dfs(v,u);
que[++idx]=u;
}
}
void st(){
for(int i=1;i<=idx;i++) f[i][0]=que[i];
for(int j=1;j<=20;j++){
for(int i=1;i+(1<<j)<=idx;i++){
int f1=f[i][j-1],f2=f[i+(1<<(j-1))][j-1];
if(dep[f1]<dep[f2]) f[i][j]=f1;
else f[i][j]=f2;
}
}
}
int LCA(int a,int b){
if(dfn[a]>dfn[b]) swap(a,b);
a=dfn[a],b=dfn[b];
int len=lg[b-a+1];
int f1=f[a][len],f2=f[b-(1<<len)+1][len];
if(dep[f1]<dep[f2]) return f1;
else return f2;
}
int getDis(int a,int b){
return dep[a]+dep[b]-2*dep[LCA(a,b)];
}
int col[N],cnt;
struct trNode{
int x,y,d;
trNode(int xx=0,int yy=0){
x=xx,y=yy;
if(x&&y) d=getDis(x,y);
else d=-INF;
}
bool operator <(const trNode &t) const{
return d<t.d;
}
};
struct Segtr{
trNode tr[4*N];
void pushup(int p){
trNode l=tr[2*p],r=tr[2*p+1];
trNode res;
if(l.x==0) res=r;
else if(r.x==0) res=l;
else{
res=max(l,r);
res=max(res,trNode(l.x,r.y));
res=max(res,trNode(l.y,r.x));
res=max(res,trNode(l.x,r.x));
res=max(res,trNode(l.y,r.y));
}
tr[p]=res;
}
void build(int p,int l,int r){
if(l==r){
tr[p]=trNode(l,l);
col[l]=1;
return ;
}
int mid=(l+r)/2;
build(2*p,l,mid);
build(2*p+1,mid+1,r);
pushup(p);
}
void update(int p,int l,int r,int x){
if(l==r){
col[l]^=1;
if(col[l]) tr[p]=trNode(l,l),cnt++;
else tr[p]=trNode(0,0),cnt--;
return ;
}
int mid=(l+r)/2;
if(x<=mid) update(2*p,l,mid,x);
else update(2*p+1,mid+1,r,x);
pushup(p);
}
}seg;
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;
lg[1]=0;
for(int i=2;i<2*N;i++) lg[i]=lg[i>>1]+1;
for(int i=1;i<=n-1;i++){
int u,v;
cin>>u>>v;
Add(u,v),Add(v,u);
}
dfs(1,0);
st();
seg.build(1,1,n);
cnt=n;
cin>>q;
for(int i=1;i<=q;i++){
char c;
cin>>c;
if(c=='C'){
int x;
cin>>x;
seg.update(1,1,n,x);
}
else{
if(cnt==0) cout<<"-1\n";
else if(cnt==1) cout<<"0\n";
else cout<<seg.tr[1].d<<'\n';
}
}
return 0;
}

浙公网安备 33010602011771号