1. 程式人生 > >【ZJOI2007】捉迷藏

【ZJOI2007】捉迷藏

題面

Description

Jiajia和Wind是一對恩愛的夫妻,並且他們有很多孩子。

某天,Jiajia、Wind和孩子們決定在家裡玩捉迷藏遊戲。

他們的家很大且構造很奇特,由N個屋子和N-1條雙向走廊組成,

這N-1條走廊的分佈使得任意兩個屋子都互相可達。

遊戲是這樣進行的,孩子們負責躲藏,Jiajia負責找,而Wind負責操縱這N個屋子的燈。

在起初的時候,所有的燈都沒有被開啟。

每一次,孩子們只會躲藏在沒有開燈的房間中,

但是為了增加刺激性,孩子們會要求開啟某個房間的電燈或者關閉某個房間的電燈。

為了評估某一次遊戲的複雜性,

Jiajia希望知道可能的最遠的兩個孩子的距離(即最遠的兩個關燈房間的距離)。

我們將以如下形式定義每一種操作:

  img

Input

輸入檔案hide.in第一行包含一個整數N,表示房間的個數,房間將被編號為1,2,3…N的整數。

接下來N-1行每行兩個整數a, b,表示房間a與房間b之間有一條走廊相連。

接下來一行包含一個整數Q,表示操作次數。.

接著Q行,每行一個操作,如上文所示。

Output

對於每一個操作Game,輸出一個非負整數到hide.out,表示最遠的兩個關燈房間的距離。

若只有一個房間是關著燈的,輸出0;若所有房間的燈都開著,輸出-1。

Sample Input

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

Sample Output

4

3

3

4

Hint

對於20%的資料,N≤50, Q≤100;

對於60%的資料,N≤3000, Q≤10000;

對於100%的資料,N≤100000, Q≤500000。

題目分析

動態詢問多組點對資訊,所以使用動態點分治。

根據點分治的基本思路,

對於一個重心,我們通常記錄經過重心的路徑來統計答案。

這裡我們沿用這種思路,設亮著的房間為白點,黑著的房間為黑點。

對於某個重心\(w\),記錄每個子樹\(v\)中的黑點到\(w\)的最長鏈(每個子樹只能貢獻一個,避免重複)。

計算該重心對答案的貢獻時直接取堆\(c[w]\)

中的最大值+次大值即可。


由於有刪除操作,我們要能夠迅速更新最長鏈的方法。

於是,我們對每個節點\(v\),再開一個堆\(up\),記錄以\(v\)為根的子樹到\(v\)\(parent\)的最長鏈。

注意:

上面的\(parent\)指的是點分樹上的父親。

然後再維護一個\(ans\)堆記錄答案。


還有一個注意事項,由於要支援堆中的刪除操作,需要額外建一個堆維護刪除資訊。

程式碼實現

#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
#include<iomanip>
#include<cstdlib>
#include<queue>
#define MAXN 0x7fffffff
typedef long long LL;
const int N=100005;
using namespace std;
inline int Getint(){register int x=0,f=1;register char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}return x*f;}
int h[N],cnt;
struct Edge{int to,next;}g[N<<1];
void AddEdge(int x,int y){g[++cnt].to=y,g[cnt].next=h[x],h[x]=cnt;}

int fa[N],dep[N];
int size[N],son[N],tp[N];
void Dfs1(int x){
    size[x]=1;
    for(int i=h[x];i;i=g[i].next){
        int to=g[i].to;
        if(to==fa[x])continue;
        fa[to]=x,dep[to]=dep[x]+1;
        Dfs1(to),size[x]+=size[to];
        if(size[to]>size[son[x]])son[x]=to;
    }
}
void Dfs2(int x,int top){
    tp[x]=top;
    if(son[x])Dfs2(son[x],top);
    for(int i=h[x];i;i=g[i].next){
        int to=g[i].to;
        if(to==fa[x]||to==son[x])continue;
        Dfs2(to,to);
    }
}
int LCA(int x,int y){
    while(tp[x]^tp[y]){
        if(dep[tp[x]]<dep[tp[y]])y=fa[tp[y]];
        else x=fa[tp[x]];
    }
    return dep[x]<dep[y]?x:y;
}
int dis(int x,int y){return dep[x]+dep[y]-2*dep[LCA(x,y)];}

struct Heap{
    priority_queue<int>q,del;
    void push(int x){q.push(x);}
    void erase(int x){del.push(x);}
    int top(){
        while(del.size()&&q.top()==del.top())q.pop(),del.pop();
        return q.top();
    }
    void Pop(){
        while(del.size()&&q.top()==del.top())q.pop(),del.pop();
        q.pop();
    }
    int sec(){
        int tmp=top();Pop();
        int x=top();push(tmp);
        return x;
    }
    int size(){return q.size()-del.size();}
}c[N],up[N],ans;

int n,num,sum,prt[N];
int rt,sz[N],mx[N];
bool vis[N],light[N];
void to_ans(int v,bool f){
    if(c[v].size()>1){
        int x=c[v].top()+c[v].sec();
        f?ans.push(x):ans.erase(x);
    }
}
void Explore(int x,int fa,int top){
    up[top].push(dis(x,prt[top]));
    for(int i=h[x];i;i=g[i].next){
        int to=g[i].to;
        if(vis[to]||to==fa)continue;
        Explore(to,x,top);
    }
}
void Get_size(int x,int fa) {
    sz[x]=1;
    for(int i=h[x];i;i=g[i].next) {
        int to=g[i].to;
        if(to==fa||vis[to]) continue;
        Get_size(to,x);
        sz[x]+=sz[to];
    }
}
void Get_root(int x,int fa){
    mx[x]=0;
    for(int i=h[x];i;i=g[i].next){
        int to=g[i].to;
        if(to==fa||vis[to])continue;
        Get_root(to,x);
        mx[x]=max(mx[x],sz[to]);
    }
    mx[x]=max(mx[x],sum-sz[x]);
    if(mx[rt]>mx[x])rt=x;
}
void Divide(int x){
    vis[x]=1,c[x].push(0);
    for(int i=h[x];i;i=g[i].next){
        int to=g[i].to;
        if(vis[to]||to==prt[x])continue;
        Get_size(to,0),sum=sz[to];
        rt=0,Get_root(to,0);
        prt[rt]=x;
        Explore(rt,0,rt);
        c[x].push(up[rt].top());
        Divide(rt);
    }
    to_ans(x,1);
}
void Change(int x,bool f){
    f?(num--):(num++);
    to_ans(x,0);
    f?c[x].erase(0):c[x].push(0);
    to_ans(x,1);
    for(int i=x;prt[i];i=prt[i]){
        to_ans(prt[i],0);
        if(up[i].size())c[prt[i]].erase(up[i].top());
        f?up[i].erase(dis(x,prt[i])):up[i].push(dis(x,prt[i]));
        if(up[i].size())c[prt[i]].push(up[i].top());
        to_ans(prt[i],1);
    }
}
int main(){
    num=n=Getint();
    for(int i=1;i<n;i++){
        int x=Getint(),y=Getint();
        AddEdge(x,y),AddEdge(y,x);
    }
    dep[1]=1,Dfs1(1),Dfs2(1,1);
    sum=n,mx[0]=n+1;
    Get_size(1,0),Get_root(1,0);
    Divide(rt);
    int m=Getint();
    for(int i=1;i<=m;i++){
        char ch=getchar();
        while(ch!='C'&&ch!='G')ch=getchar();
        if(ch=='C'){
            int x=Getint();
            light[x]^=1;
            Change(x,light[x]);
        }else{
            if(!num)cout<<"-1\n";
            else if(num==1)cout<<"0\n";
            else cout<<ans.top()<<'\n';
        }
    }
    return 0;
}