1095: [ZJOI2007]Hide 捉迷藏

BZOJ

题意

给了一颗 n n 个节点的树,起初树上节点的颜色都是黑的,有两种操作,第一种是把某个节点变色,黑的变白,白的变黑,第二种是查询当前树上最远的两个黑色节点的距离,不存在黑点输出 1 -1 ;

题解

这题本来是一道经典的点分治题,但是在做机房dalao给的题时,学了一种更优秀的做法:用线段树维护树上直径;先DFS一遍得到树的DFS序,以DFS序建一棵线段树;现在考虑,如果得到了两棵子树内的直径,两棵子树合并起来会是怎样,结果是显然的,新直径的两个端点必然是原先四个直径端点中的两个,那么就可以两两枚举这两个点找到新的直径,这就是线段树的 P u s h u p Pushup 函数,因为这个做法显然能够推广到任意两个树上联通块的合并上;如果再预处理 S T ST O ( 1 ) O(1) L C A LCA 的话,这个做法的复杂度为 O ( n l o g n ) O(nlogn) ,并且支持修改和子树查询;

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int n,m,p,Q,cnt,TIME;
int head[N],Log[N<<1],ST[22][N<<1],V[N<<1],ID[N<<1],dep[N],S[N],dfn[N],low[N];
char ins[5];
struct Data {
    int u,v;
    Data () {}
    Data (int a,int b) :u(a),v(b) {}
}G[N<<2],ANS;
int LCA(int u,int v) {
    if(ID[u]>ID[v]) swap(u,v);
    int k=Log[ID[v]-ID[u]+1];
   	if(dep[ST[k][ID[u]]]<dep[ST[k][ID[v]-(1<<k)+1]]) return ST[k][ID[u]];
   	return ST[k][ID[v]-(1<<k)+1];
}
Data operator +(Data A,Data B) {
    if(!A.u) return B;
    if(!B.u) return A;
    int d1,d2,d3,d4,d5,d6,MAXV;
    d1=dep[A.u]+dep[A.v]-2*dep[LCA(A.u,A.v)];
    d2=dep[B.u]+dep[B.v]-2*dep[LCA(B.u,B.v)];
    d3=dep[A.u]+dep[B.v]-2*dep[LCA(A.u,B.v)];
    d4=dep[B.u]+dep[A.v]-2*dep[LCA(B.u,A.v)];
    d5=dep[A.u]+dep[B.u]-2*dep[LCA(A.u,B.u)];
    d6=dep[A.v]+dep[B.v]-2*dep[LCA(A.v,B.v)];
    MAXV=max(max(max(max(max(d1,d2),d3),d4),d5),d6);
    if(MAXV==d1) return Data(A.u,A.v);
    if(MAXV==d2) return Data(B.u,B.v);
    if(MAXV==d3) return Data(A.u,B.v);
    if(MAXV==d4) return Data(B.u,A.v);
    if(MAXV==d5) return Data(A.u,B.u);
    return Data(A.v,B.v);
}
#define L o<<1
#define R o<<1|1
struct SegmentTree {
    bool On[N<<2];
    void Build(int l,int r,int o) {
        if(l==r) {
            On[o]=true;
            G[o]=Data(S[l],S[l]);
            return;
        }
        int mid=l+r>>1;
        Build(l,mid,L); Build(mid+1,r,R);
        G[o]=G[L]+G[R];
    }
    void Modify(int l,int r,int o,int pos) {
        if(l==r) {
            On[o]^=1;
            if(On[o]) G[o]=Data(S[l],S[l]);
            else G[o]=Data(0,0);
            return;
        }
        int mid=l+r>>1;
        if(pos<=mid) Modify(l,mid,L,pos);
        else Modify(mid+1,r,R,pos);
        G[o]=G[L]+G[R];
    }
    void Query(int l,int r,int o,int ql,int qr) {
        if(ql<=l&&r<=qr) {
            ANS=ANS+G[o];
            return;
        }
        int mid=l+r>>1;
        if(ql<=mid) Query(l,mid,L,ql,qr);
        if(qr>mid) Query(mid+1,r,R,ql,qr);
    }
}SgT;
struct Edge {
    int to,last;
    Edge () {}
    Edge (int a,int b) :to(a),last(b) {}
}edge[N<<1];
void ADD(int a,int b) {
    edge[++p]=Edge(b,head[a]); head[a]=p;
    edge[++p]=Edge(a,head[b]); head[b]=p;
}
void DFS(int u,int fa) {
    dfn[u]=++TIME; S[TIME]=u; ID[u]=++cnt; V[cnt]=u;
    for(int i=head[u];i;i=edge[i].last) {
        int v=edge[i].to;
        if(v!=fa) {
            dep[v]=dep[u]+1; DFS(v,u);
            V[++cnt]=u;
        }
    }
    low[u]=TIME;
}
void Prepare() {
    Log[0]=-1;
    for(int i=1;i<=cnt;++i) Log[i]=Log[i>>1]+1;
    for(int i=1;i<=cnt;++i) ST[0][i]=V[i];
    for(int i=1;(1<<i)<=cnt;++i) {
        for(int j=1;j+(1<<i)-1<=cnt;++j) {
            if(dep[ST[i-1][j]]<dep[ST[i-1][j+(1<<(i-1))]]) ST[i][j]=ST[i-1][j];
            else ST[i][j]=ST[i-1][j+(1<<(i-1))];
        }
    }
}
int main() {
    scanf("%d",&n);
    for(int i=1;i<n;++i) {
        int u,v; scanf("%d%d",&u,&v);
        ADD(u,v);
    }
    DFS(1,0);
    Prepare();
    SgT.Build(1,n,1);
    scanf("%d",&Q);
    while(Q--) {
        int x; scanf("%s",ins);
        if(ins[0]=='C') scanf("%d",&x),SgT.Modify(1,n,1,dfn[x]);
        else {
            if(!G[1].u) puts("-1");
            else printf("%d\n",dep[G[1].u]+dep[G[1].v]-2*dep[LCA(G[1].u,G[1].v)]);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/CIao_015/article/details/83576846