Loading

【题解】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;
}
posted @ 2026-06-04 23:08  Seqfrel  阅读(41)  评论(0)    收藏  举报