1. 程式人生 > >1095: [ZJOI2007]Hide 捉迷藏

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;
}