1. 程式人生 > >Query on a tree V(SPOJ - QTREE5)

Query on a tree V(SPOJ - QTREE5)

esp tin init include 長度 輸入 head using 父親

題目描述:

You are given a tree (an acyclic undirected connected graph) with N nodes. The tree nodes are numbered from 1 to N. We define dist(a, b) as the number of edges on the path from node a to node b.

Each node has a color, white or black. All the nodes are black initially.

We will ask you to perfrom some instructions of the following form:

  • 0 i : change the color of i-th node(from black to white, or from white to black).
  • 1 v : ask for the minimum dist(u, v), node u must be white(u can be equal to v). Obviously, as long as node v is white, the result will always be 0.

給出一棵n結點的樹,邊權為1,一開始每個點顏色都是黑色。
現有q個詢問,每次詢問會有兩種操作:
1.0 i 改變i的顏色,黑變白,白變黑。

2.1 v 詢問與v距離最近的白點,顯然,當v顏色為白色時,答案是0。

輸入

  • In the first line there is an integer N (N <= 100000)
  • In the next N-1 lines, the i-th line describes the i-th edge: a line with two integers a b denotes an edge between a and b.
  • In the next line, there is an integer Q denotes the number of instructions (Q <= 100000)
  • In the next Q lines, each line contains an instruction "0 i" or "1 v"

輸出

For each "1 v" operation, print one integer representing its result. If there is no white node in the tree, you should write "-1".

思路:

這是自己寫的第一個動態點分治題。

在每個點上都建立一個可刪除堆(手寫也可以,像我一樣偷懶的話兩個系統堆模擬也行,一個記錄隊列中加入的,一個記錄堆中被刪除的元素,每次遇到隊首相同的元素,就同時彈出即可!)這樣每個點都可以(近乎)\(O(1)\)的求出一個點上的答案,不過這樣的話,更新的復雜度會直接爆炸!

點分治

動態點分治的用處這時就體現出來了,就只用更新點分治樹樹根到該點的路徑上的所有點即可,復雜度為\(O(logn)\),然後在詢問的時候,同理在這條路徑上,枚舉每一個點,將該點隊列上的最大值再加上該點到詢問點上的距離的值求出,一路\(min\)上去最後得到的就是答案,復雜度同樣為\(O(logn)\),枚舉該路徑只要從當前點開始在點分治樹上跳父親即可

最後就是求兩點之間的路徑長度,應該不難,用\(LCA\)法即可,\(lca\)可以直接預處理,用跳重鏈法等都可以,有

dis(x,y)=dep[x]+dep[y]-dep[lca]*2

\(dep\)為該點深度。

代碼

#include<cstdio>
#include<cstring>
#include<queue>
#define FOR(i,l,r) for(int i=l,END=r;i<=END;i++)
#define DOR(i,r,l) for(int i=r,END=l;i>=END;i--)
#define loop(i,n) for(int i=0,END=n;i<END;i++)
#define mms(a,x) memset(a,x,sizeof a)
#define pf printf
#define sf scanf
using namespace std;
const int N=1e5+5;

struct Graph{//正向表 
    int tot,to[N<<1],nxt[N<<1],head[N];
    void add(int x,int y){tot++;to[tot]=y;nxt[tot]=head[x];head[x]=tot;}
    void clear(){mms(head,-1);tot=0;}
    #define EOR(i,x) for(int i=G.head[x];i!=-1;i=G.nxt[i])
}G;

struct Distance_Calculator{//計算兩點的路徑 
    int sz[N],son[N],fa[N],dep[N],top[N];
    void dfs(int x,int f){
        fa[x]=f,dep[x]=dep[f]+1;
        sz[x]=1,son[x]=0;
        EOR(i,x){
            int v=G.to[i];
            if(v==f)continue;
            dfs(v,x);
            sz[x]+=sz[v];
            if(sz[son[x]]<sz[v])son[x]=v;
//          pf("x:%d son[x]:%d\n",x,son[x]);
        }
    }
    void top_dfs(int x,int f,int tp){
//      pf("x:%d f:%d tp:%d son[x]:%d\n",x,f,tp,son[x]);
        top[x]=tp;
        if(son[x])top_dfs(son[x],x,tp);
        EOR(i,x){
            int v=G.to[i];
            if(v==f||v==son[x])continue;
            top_dfs(v,x,v);
        }
    }
    int LCA(int x,int y){//跳重鏈 
        while(top[x]!=top[y]){
            if(dep[top[x]]>dep[top[y]])x=fa[top[x]];
            else y=fa[top[y]];
        }
        return dep[y]>dep[x]?x:y;
    }
    int dist(int x,int y){return dep[x]+dep[y]-(dep[LCA(x,y)]<<1);}
    void init(){
        dfs(1,0);
        top_dfs(1,0,1);
    }
}D;

int fa[N];

struct Heap{//可刪除堆 
    priority_queue<int,vector<int>,greater<int> >Q,del;
    void Del(int x){del.push(x);}
    void Erase(){while(!del.empty()&&del.top()==Q.top())Q.pop(),del.pop();}
    void Push(int x){Q.push(x);}
    bool Empty(){Erase();return Q.empty();}
    int Top(){Erase();if(Empty())return -1;else return Q.top();}
    
}H[N];

int n;
bool vis[N];
int sz[N],mx[N],center,t_sz;
void get_center(int x,int f){//找重心 
    sz[x]=1,mx[x]=0;
    EOR(i,x){
        int v=G.to[i];
        if(v==f||vis[v])continue;
        get_center(v,x);
        sz[x]+=sz[v];
        mx[x]=max(mx[x],sz[v]);
    }
    mx[x]=max(mx[x],t_sz-sz[x]);
    if(!center||mx[center]>mx[x])center=x;
}
void DAC(int x){//預處理點分樹 
    vis[x]=1;
    EOR(i,x){
        int v=G.to[i];
        if(vis[v])continue;
        t_sz=sz[v],center=0;
        get_center(v,x);
        fa[center]=x;//關鍵 
        DAC(center);
    }
}

bool col[N];//記錄顏色 

void insert(int x){
    H[x].Push(0);//加入自己本身 
    for(int i=fa[x];i;i=fa[i]){//暴跳父親,更新 
        int dis=D.dist(x,i);
        H[i].Push(dis);
    }
}
void erase(int x){
    H[x].Del(0);//刪除自己本身 
    for(int i=fa[x];i;i=fa[i]){//暴跳父親,刪除 
        int dis=D.dist(x,i);
        H[i].Del(dis);
    }
}
int Query(int x){//詢問 
    int ret=2e9;
    for(int i=x;i;i=fa[i]){
        int dis=D.dist(x,i);
        int dis2=H[i].Top();
        if(dis2==-1)continue;
        ret=min(ret,dis2+dis);
    }
    return ret==2e9?-1:ret;
}
int main(){
    G.clear();
    sf("%d",&n);
    FOR(i,1,n-1){
        int x,y;
        sf("%d%d",&x,&y);
        G.add(x,y);
        G.add(y,x);
    }
    t_sz=n,get_center(1,0);
    DAC(center);
    D.init();
    //以上為預處理 
    int q;
    sf("%d",&q);
    while(q--){
        int op,v;
        sf("%d%d",&op,&v);
        if(!op){
            col[v]^=1;
            if(col[v])insert(v);
            else erase(v);
        }
        else pf("%d\n",Query(v));
    }
    return 0;
}

Query on a tree V(SPOJ - QTREE5)